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  }