github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/provider/lxd/config_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // +build go1.3 5 6 package lxd_test 7 8 import ( 9 "fmt" 10 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 "gopkg.in/juju/environschema.v1" 14 15 "github.com/juju/juju/environs" 16 "github.com/juju/juju/environs/config" 17 "github.com/juju/juju/provider/lxd" 18 "github.com/juju/juju/testing" 19 "github.com/juju/juju/tools/lxdclient" 20 ) 21 22 type configSuite struct { 23 lxd.BaseSuite 24 25 config *config.Config 26 } 27 28 var _ = gc.Suite(&configSuite{}) 29 30 func (s *configSuite) SetUpTest(c *gc.C) { 31 s.BaseSuite.SetUpTest(c) 32 33 cfg, err := testing.ModelConfig(c).Apply(lxd.ConfigAttrs) 34 c.Assert(err, jc.ErrorIsNil) 35 s.config = cfg 36 } 37 38 func (s *configSuite) TestDefaults(c *gc.C) { 39 cfg := lxd.NewBaseConfig(c) 40 ecfg := lxd.NewConfig(cfg) 41 42 values, extras := ecfg.Values(c) 43 c.Assert(extras, gc.HasLen, 0) 44 45 c.Check(values, jc.DeepEquals, lxd.ConfigValues{ 46 Namespace: cfg.Name(), 47 RemoteURL: "", 48 ClientCert: "", 49 ClientKey: "", 50 ServerCert: "", 51 }) 52 } 53 54 func (s *configSuite) TestClientConfigLocal(c *gc.C) { 55 cfg := lxd.NewBaseConfig(c) 56 ecfg := lxd.NewConfig(cfg) 57 values, _ := ecfg.Values(c) 58 c.Assert(values.RemoteURL, gc.Equals, "") 59 60 clientCfg, err := ecfg.ClientConfig() 61 c.Assert(err, jc.ErrorIsNil) 62 63 c.Check(clientCfg, jc.DeepEquals, lxdclient.Config{ 64 Namespace: cfg.Name(), 65 Remote: lxdclient.Remote{ 66 Name: "juju-remote", 67 Host: "", 68 Protocol: lxdclient.LXDProtocol, 69 Cert: nil, 70 ServerPEMCert: "", 71 }, 72 }) 73 } 74 75 func (s *configSuite) TestClientConfigNonLocal(c *gc.C) { 76 cfg := lxd.NewBaseConfig(c) 77 ecfg := lxd.NewConfig(cfg) 78 ecfg = ecfg.Apply(c, map[string]interface{}{ 79 "remote-url": "10.0.0.1", 80 "client-cert": "<a valid x.509 cert>", 81 "client-key": "<a valid x.509 key>", 82 "server-cert": "<a valid x.509 server cert>", 83 }) 84 85 clientCfg, err := ecfg.ClientConfig() 86 c.Assert(err, jc.ErrorIsNil) 87 88 c.Check(clientCfg, jc.DeepEquals, lxdclient.Config{ 89 Namespace: cfg.Name(), 90 Remote: lxdclient.Remote{ 91 Name: "juju-remote", 92 Host: "10.0.0.1", 93 Protocol: lxdclient.LXDProtocol, 94 Cert: &lxdclient.Cert{ 95 Name: fmt.Sprintf("juju cert for env %q", s.config.Name()), 96 CertPEM: []byte("<a valid x.509 cert>"), 97 KeyPEM: []byte("<a valid x.509 key>"), 98 }, 99 ServerPEMCert: "<a valid x.509 server cert>", 100 }, 101 }) 102 } 103 104 func (s *configSuite) TestUpdateForClientConfigLocal(c *gc.C) { 105 cfg := lxd.NewBaseConfig(c) 106 ecfg := lxd.NewConfig(cfg) 107 108 clientCfg, err := ecfg.ClientConfig() 109 c.Assert(err, jc.ErrorIsNil) 110 updated, err := ecfg.UpdateForClientConfig(clientCfg) 111 c.Assert(err, jc.ErrorIsNil) 112 113 values, extras := updated.Values(c) 114 c.Assert(extras, gc.HasLen, 0) 115 116 c.Check(values, jc.DeepEquals, lxd.ConfigValues{ 117 Namespace: cfg.Name(), 118 RemoteURL: "", 119 ClientCert: "", 120 ClientKey: "", 121 ServerCert: "", 122 }) 123 } 124 125 func (s *configSuite) TestUpdateForClientConfigNonLocal(c *gc.C) { 126 cfg := lxd.NewBaseConfig(c) 127 ecfg := lxd.NewConfig(cfg) 128 ecfg = ecfg.Apply(c, map[string]interface{}{ 129 "remote-url": "10.0.0.1", 130 "client-cert": "<a valid x.509 cert>", 131 "client-key": "<a valid x.509 key>", 132 "server-cert": "<a valid x.509 server cert>", 133 }) 134 135 before, extras := ecfg.Values(c) 136 c.Assert(extras, gc.HasLen, 0) 137 138 clientCfg, err := ecfg.ClientConfig() 139 c.Assert(err, jc.ErrorIsNil) 140 updated, err := ecfg.UpdateForClientConfig(clientCfg) 141 c.Assert(err, jc.ErrorIsNil) 142 143 after, extras := updated.Values(c) 144 c.Assert(extras, gc.HasLen, 0) 145 146 c.Check(before, jc.DeepEquals, lxd.ConfigValues{ 147 Namespace: cfg.Name(), 148 RemoteURL: "10.0.0.1", 149 ClientCert: "<a valid x.509 cert>", 150 ClientKey: "<a valid x.509 key>", 151 ServerCert: "<a valid x.509 server cert>", 152 }) 153 c.Check(after, jc.DeepEquals, lxd.ConfigValues{ 154 Namespace: cfg.Name(), 155 RemoteURL: "10.0.0.1", 156 ClientCert: "<a valid x.509 cert>", 157 ClientKey: "<a valid x.509 key>", 158 ServerCert: "<a valid x.509 server cert>", 159 }) 160 } 161 162 func (s *configSuite) TestUpdateForClientConfigGeneratedCert(c *gc.C) { 163 cfg := lxd.NewBaseConfig(c) 164 ecfg := lxd.NewConfig(cfg) 165 ecfg = ecfg.Apply(c, map[string]interface{}{ 166 "remote-url": "10.0.0.1", 167 "client-cert": "", 168 "client-key": "", 169 "server-cert": "", 170 }) 171 172 before, extras := ecfg.Values(c) 173 c.Assert(extras, gc.HasLen, 0) 174 175 clientCfg, err := ecfg.ClientConfig() 176 c.Assert(err, jc.ErrorIsNil) 177 updated, err := ecfg.UpdateForClientConfig(clientCfg) 178 c.Assert(err, jc.ErrorIsNil) 179 180 after, extras := updated.Values(c) 181 c.Assert(extras, gc.HasLen, 0) 182 183 c.Check(before, jc.DeepEquals, lxd.ConfigValues{ 184 Namespace: cfg.Name(), 185 RemoteURL: "10.0.0.1", 186 ClientCert: "", 187 ClientKey: "", 188 ServerCert: "", 189 }) 190 after.CheckCert(c) 191 after.ClientCert = "" 192 after.ClientKey = "" 193 after.ServerCert = "" 194 c.Check(after, jc.DeepEquals, lxd.ConfigValues{ 195 Namespace: cfg.Name(), 196 RemoteURL: "10.0.0.1", 197 ClientCert: "", 198 ClientKey: "", 199 ServerCert: "", 200 }) 201 } 202 203 // TODO(ericsnow) Each test only deals with a single field, so having 204 // multiple values in insert and remove (in configTestSpec) is a little 205 // misleading and unecessary. 206 207 // configTestSpec defines a subtest to run in a table driven test. 208 type configTestSpec struct { 209 // info describes the subtest. 210 info string 211 // insert holds attrs that should be merged into the config. 212 insert testing.Attrs 213 // remove has the names of attrs that should be removed. 214 remove []string 215 // expect defines the expected attributes in a success case. 216 expect testing.Attrs 217 // err is the error message to expect in a failure case. 218 err string 219 } 220 221 func (ts configTestSpec) checkSuccess(c *gc.C, value interface{}, err error) { 222 if !c.Check(err, jc.ErrorIsNil) { 223 return 224 } 225 226 var cfg *config.Config 227 switch typed := value.(type) { 228 case *config.Config: 229 cfg = typed 230 case environs.Environ: 231 cfg = typed.Config() 232 } 233 234 attrs := cfg.AllAttrs() 235 for field, value := range ts.expect { 236 c.Check(attrs[field], gc.Equals, value) 237 } 238 } 239 240 func (ts configTestSpec) checkFailure(c *gc.C, err error, msg string) { 241 c.Check(err, gc.ErrorMatches, msg+": "+ts.err) 242 } 243 244 func (ts configTestSpec) checkAttrs(c *gc.C, attrs map[string]interface{}, cfg *config.Config) { 245 for field, expected := range cfg.UnknownAttrs() { 246 value := attrs[field] 247 c.Check(value, gc.Equals, expected) 248 } 249 } 250 251 func (ts configTestSpec) attrs() testing.Attrs { 252 attrs := lxd.ConfigAttrs 253 return attrs.Merge(ts.insert).Delete(ts.remove...) 254 } 255 256 func (ts configTestSpec) newConfig(c *gc.C) *config.Config { 257 attrs := ts.attrs() 258 cfg, err := testing.ModelConfig(c).Apply(attrs) 259 c.Assert(err, jc.ErrorIsNil) 260 return cfg 261 } 262 263 func (ts configTestSpec) fixCfg(c *gc.C, cfg *config.Config) *config.Config { 264 fixes := make(map[string]interface{}) 265 266 // Set changed values. 267 fixes = updateAttrs(fixes, ts.insert) 268 269 newCfg, err := cfg.Apply(fixes) 270 c.Assert(err, jc.ErrorIsNil) 271 return newCfg 272 } 273 274 func updateAttrs(attrs, updates testing.Attrs) testing.Attrs { 275 updated := make(testing.Attrs, len(attrs)) 276 for k, v := range attrs { 277 updated[k] = v 278 } 279 for k, v := range updates { 280 updated[k] = v 281 } 282 return updated 283 } 284 285 var newConfigTests = []configTestSpec{{ 286 info: "namespace is optional", 287 remove: []string{"namespace"}, 288 expect: testing.Attrs{"namespace": "testenv"}, 289 }, { 290 info: "namespace can be empty", 291 insert: testing.Attrs{"namespace": ""}, 292 expect: testing.Attrs{"namespace": "testenv"}, 293 }, { 294 info: "remote-url is optional", 295 remove: []string{"remote-url"}, 296 expect: testing.Attrs{"remote-url": ""}, 297 }, { 298 info: "remote-url can be empty", 299 insert: testing.Attrs{"remote-url": ""}, 300 expect: testing.Attrs{"remote-url": ""}, 301 }, { 302 info: "client-cert is optional", 303 remove: []string{"client-cert"}, 304 expect: testing.Attrs{"client-cert": ""}, 305 }, { 306 info: "client-cert can be empty", 307 insert: testing.Attrs{"client-cert": ""}, 308 expect: testing.Attrs{"client-cert": ""}, 309 }, { 310 info: "client-key is optional", 311 remove: []string{"client-key"}, 312 expect: testing.Attrs{"client-key": ""}, 313 }, { 314 info: "client-key can be empty", 315 insert: testing.Attrs{"client-key": ""}, 316 expect: testing.Attrs{"client-key": ""}, 317 }, { 318 info: "server-cert is optional", 319 remove: []string{"server-cert"}, 320 expect: testing.Attrs{"server-cert": ""}, 321 }, { 322 info: "unknown field is not touched", 323 insert: testing.Attrs{"unknown-field": 12345}, 324 expect: testing.Attrs{"unknown-field": 12345}, 325 }} 326 327 func (s *configSuite) TestNewModelConfig(c *gc.C) { 328 // TODO(ericsnow) Move to a functional suite. 329 if !s.IsRunningLocally(c) { 330 c.Skip("LXD not running locally") 331 } 332 333 // TODO(redir): Remove after wily or in yakkety. 334 skipIfWily(c) 335 336 for i, test := range newConfigTests { 337 c.Logf("test %d: %s", i, test.info) 338 339 testConfig := test.newConfig(c) 340 environ, err := environs.New(testConfig) 341 342 // Check the result 343 if test.err != "" { 344 test.checkFailure(c, err, "invalid config") 345 } else { 346 test.checkSuccess(c, environ, err) 347 } 348 } 349 } 350 351 // TODO(wwitzel3) refactor to provider_test file 352 func (s *configSuite) TestValidateNewConfig(c *gc.C) { 353 for i, test := range newConfigTests { 354 c.Logf("test %d: %s", i, test.info) 355 356 testConfig := test.newConfig(c) 357 validatedConfig, err := lxd.Provider.Validate(testConfig, nil) 358 359 // Check the result 360 if test.err != "" { 361 test.checkFailure(c, err, "invalid config") 362 } else { 363 c.Check(validatedConfig, gc.NotNil) 364 test.checkSuccess(c, validatedConfig, err) 365 } 366 } 367 } 368 369 // TODO(wwitzel3) refactor to the provider_test file 370 func (s *configSuite) TestValidateOldConfig(c *gc.C) { 371 for i, test := range newConfigTests { 372 c.Logf("test %d: %s", i, test.info) 373 374 oldcfg := test.newConfig(c) 375 var err error 376 oldcfg, err = lxd.Provider.Validate(oldcfg, nil) 377 c.Assert(err, jc.ErrorIsNil) 378 newcfg := test.fixCfg(c, s.config) 379 expected := updateAttrs(lxd.ConfigAttrs, test.insert) 380 381 // Validate the new config (relative to the old one) using the 382 // provider. 383 validatedConfig, err := lxd.Provider.Validate(newcfg, oldcfg) 384 385 // Check the result. 386 if test.err != "" { 387 test.checkFailure(c, err, "invalid base config") 388 } else { 389 if !c.Check(err, jc.ErrorIsNil) { 390 continue 391 } 392 // We verify that Validate filled in the defaults 393 // appropriately. 394 c.Check(validatedConfig, gc.NotNil) 395 test.checkAttrs(c, expected, validatedConfig) 396 } 397 } 398 } 399 400 // TODO(ericsnow) Add tests for client-cert and client-key. 401 402 var changeConfigTests = []configTestSpec{{ 403 info: "no change, no error", 404 expect: lxd.ConfigAttrs, 405 }, { 406 info: "cannot change namespace", 407 insert: testing.Attrs{"namespace": "spam"}, 408 err: "namespace: cannot change from testenv to spam", 409 //}, { 410 // TODO(ericsnow) This triggers cert generation... 411 // info: "cannot change remote-url", 412 // insert: testing.Attrs{"remote-url": "eggs"}, 413 // err: "remote-url: cannot change from to eggs", 414 }, { 415 info: "can insert unknown field", 416 insert: testing.Attrs{"unknown": "ignoti"}, 417 expect: testing.Attrs{"unknown": "ignoti"}, 418 }} 419 420 // TODO(wwitzel3) refactor this to the provider_test file. 421 func (s *configSuite) TestValidateChange(c *gc.C) { 422 for i, test := range changeConfigTests { 423 c.Logf("test %d: %s", i, test.info) 424 425 testConfig := test.newConfig(c) 426 validatedConfig, err := lxd.Provider.Validate(testConfig, s.config) 427 428 // Check the result. 429 if test.err != "" { 430 test.checkFailure(c, err, "invalid config change") 431 } else { 432 test.checkSuccess(c, validatedConfig, err) 433 } 434 } 435 } 436 437 func (s *configSuite) TestSetConfig(c *gc.C) { 438 // TODO(ericsnow) Move to a functional suite. 439 if !s.IsRunningLocally(c) { 440 c.Skip("LXD not running locally") 441 } 442 443 // TODO(redir): Remove after wily or in yakkety. 444 skipIfWily(c) 445 446 for i, test := range changeConfigTests { 447 c.Logf("test %d: %s", i, test.info) 448 449 environ, err := environs.New(s.config) 450 c.Assert(err, jc.ErrorIsNil) 451 452 testConfig := test.newConfig(c) 453 err = environ.SetConfig(testConfig) 454 455 // Check the result. 456 if test.err != "" { 457 test.checkFailure(c, err, "invalid config change") 458 expected, err := lxd.Provider.Validate(s.config, nil) 459 c.Assert(err, jc.ErrorIsNil) 460 test.checkAttrs(c, environ.Config().AllAttrs(), expected) 461 } else { 462 test.checkSuccess(c, environ.Config(), err) 463 } 464 } 465 } 466 467 func (*configSuite) TestSchema(c *gc.C) { 468 fields := lxd.Provider.(interface { 469 Schema() environschema.Fields 470 }).Schema() 471 // Check that all the fields defined in environs/config 472 // are in the returned schema. 473 globalFields, err := config.Schema(nil) 474 c.Assert(err, gc.IsNil) 475 for name, field := range globalFields { 476 c.Check(fields[name], jc.DeepEquals, field) 477 } 478 }