github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 for i, test := range newConfigTests { 334 c.Logf("test %d: %s", i, test.info) 335 336 testConfig := test.newConfig(c) 337 environ, err := environs.New(testConfig) 338 339 // Check the result 340 if test.err != "" { 341 test.checkFailure(c, err, "invalid config") 342 } else { 343 test.checkSuccess(c, environ, err) 344 } 345 } 346 } 347 348 // TODO(wwitzel3) refactor to provider_test file 349 func (s *configSuite) TestValidateNewConfig(c *gc.C) { 350 for i, test := range newConfigTests { 351 c.Logf("test %d: %s", i, test.info) 352 353 testConfig := test.newConfig(c) 354 validatedConfig, err := lxd.Provider.Validate(testConfig, nil) 355 356 // Check the result 357 if test.err != "" { 358 test.checkFailure(c, err, "invalid config") 359 } else { 360 c.Check(validatedConfig, gc.NotNil) 361 test.checkSuccess(c, validatedConfig, err) 362 } 363 } 364 } 365 366 // TODO(wwitzel3) refactor to the provider_test file 367 func (s *configSuite) TestValidateOldConfig(c *gc.C) { 368 for i, test := range newConfigTests { 369 c.Logf("test %d: %s", i, test.info) 370 371 oldcfg := test.newConfig(c) 372 var err error 373 oldcfg, err = lxd.Provider.Validate(oldcfg, nil) 374 c.Assert(err, jc.ErrorIsNil) 375 newcfg := test.fixCfg(c, s.config) 376 expected := updateAttrs(lxd.ConfigAttrs, test.insert) 377 378 // Validate the new config (relative to the old one) using the 379 // provider. 380 validatedConfig, err := lxd.Provider.Validate(newcfg, oldcfg) 381 382 // Check the result. 383 if test.err != "" { 384 test.checkFailure(c, err, "invalid base config") 385 } else { 386 if !c.Check(err, jc.ErrorIsNil) { 387 continue 388 } 389 // We verify that Validate filled in the defaults 390 // appropriately. 391 c.Check(validatedConfig, gc.NotNil) 392 test.checkAttrs(c, expected, validatedConfig) 393 } 394 } 395 } 396 397 // TODO(ericsnow) Add tests for client-cert and client-key. 398 399 var changeConfigTests = []configTestSpec{{ 400 info: "no change, no error", 401 expect: lxd.ConfigAttrs, 402 }, { 403 info: "cannot change namespace", 404 insert: testing.Attrs{"namespace": "spam"}, 405 err: "namespace: cannot change from testenv to spam", 406 //}, { 407 // TODO(ericsnow) This triggers cert generation... 408 // info: "cannot change remote-url", 409 // insert: testing.Attrs{"remote-url": "eggs"}, 410 // err: "remote-url: cannot change from to eggs", 411 }, { 412 info: "can insert unknown field", 413 insert: testing.Attrs{"unknown": "ignoti"}, 414 expect: testing.Attrs{"unknown": "ignoti"}, 415 }} 416 417 // TODO(wwitzel3) refactor this to the provider_test file. 418 func (s *configSuite) TestValidateChange(c *gc.C) { 419 for i, test := range changeConfigTests { 420 c.Logf("test %d: %s", i, test.info) 421 422 testConfig := test.newConfig(c) 423 validatedConfig, err := lxd.Provider.Validate(testConfig, s.config) 424 425 // Check the result. 426 if test.err != "" { 427 test.checkFailure(c, err, "invalid config change") 428 } else { 429 test.checkSuccess(c, validatedConfig, err) 430 } 431 } 432 } 433 434 func (s *configSuite) TestSetConfig(c *gc.C) { 435 // TODO(ericsnow) Move to a functional suite. 436 if !s.IsRunningLocally(c) { 437 c.Skip("LXD not running locally") 438 } 439 440 for i, test := range changeConfigTests { 441 c.Logf("test %d: %s", i, test.info) 442 443 environ, err := environs.New(s.config) 444 c.Assert(err, jc.ErrorIsNil) 445 446 testConfig := test.newConfig(c) 447 err = environ.SetConfig(testConfig) 448 449 // Check the result. 450 if test.err != "" { 451 test.checkFailure(c, err, "invalid config change") 452 expected, err := lxd.Provider.Validate(s.config, nil) 453 c.Assert(err, jc.ErrorIsNil) 454 test.checkAttrs(c, environ.Config().AllAttrs(), expected) 455 } else { 456 test.checkSuccess(c, environ.Config(), err) 457 } 458 } 459 } 460 461 func (*configSuite) TestSchema(c *gc.C) { 462 fields := lxd.Provider.(interface { 463 Schema() environschema.Fields 464 }).Schema() 465 // Check that all the fields defined in environs/config 466 // are in the returned schema. 467 globalFields, err := config.Schema(nil) 468 c.Assert(err, gc.IsNil) 469 for name, field := range globalFields { 470 c.Check(fields[name], jc.DeepEquals, field) 471 } 472 }