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  }