github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/provider/gce/config_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package gce_test
     5  
     6  import (
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/environs"
    15  	"github.com/juju/juju/environs/config"
    16  	"github.com/juju/juju/provider/gce"
    17  	"github.com/juju/juju/testing"
    18  )
    19  
    20  type ConfigSuite struct {
    21  	gce.BaseSuite
    22  
    23  	config  *config.Config
    24  	rootDir string
    25  }
    26  
    27  var _ = gc.Suite(&ConfigSuite{})
    28  
    29  func (s *ConfigSuite) SetUpTest(c *gc.C) {
    30  	s.BaseSuite.SetUpTest(c)
    31  
    32  	cfg, err := testing.EnvironConfig(c).Apply(gce.ConfigAttrs)
    33  	c.Assert(err, jc.ErrorIsNil)
    34  	s.config = cfg
    35  	s.rootDir = c.MkDir()
    36  }
    37  
    38  // TODO(ericsnow) Each test only deals with a single field, so having
    39  // multiple values in insert and remove (in configTestSpec) is a little
    40  // misleading and unecessary.
    41  
    42  // configTestSpec defines a subtest to run in a table driven test.
    43  type configTestSpec struct {
    44  	// info describes the subtest.
    45  	info string
    46  	// insert holds attrs that should be merged into the config.
    47  	insert testing.Attrs
    48  	// remove has the names of attrs that should be removed.
    49  	remove []string
    50  	// expect defines the expected attributes in a success case.
    51  	expect testing.Attrs
    52  	// err is the error message to expect in a failure case.
    53  	err string
    54  
    55  	// rootDir is the path to the root directory for this test.
    56  	rootDir string
    57  }
    58  
    59  func (ts configTestSpec) checkSuccess(c *gc.C, value interface{}, err error) {
    60  	if !c.Check(err, jc.ErrorIsNil) {
    61  		return
    62  	}
    63  
    64  	var cfg *config.Config
    65  	switch typed := value.(type) {
    66  	case *config.Config:
    67  		cfg = typed
    68  	case environs.Environ:
    69  		cfg = typed.Config()
    70  	}
    71  
    72  	attrs := cfg.AllAttrs()
    73  	for field, value := range ts.expect {
    74  		if field == "auth-file" && value != nil && value.(string) != "" {
    75  			value = filepath.Join(ts.rootDir, value.(string))
    76  		}
    77  		c.Check(attrs[field], gc.Equals, value)
    78  	}
    79  }
    80  
    81  func (ts configTestSpec) checkFailure(c *gc.C, err error, msg string) {
    82  	c.Check(err, gc.ErrorMatches, msg+": "+ts.err)
    83  }
    84  
    85  func (ts configTestSpec) checkAttrs(c *gc.C, attrs map[string]interface{}, cfg *config.Config) {
    86  	for field, expected := range cfg.UnknownAttrs() {
    87  		value := attrs[field]
    88  		if field == "auth-file" && value != nil {
    89  			filename := value.(string)
    90  			if filename != "" {
    91  				value = interface{}(filepath.Join(ts.rootDir, filename))
    92  			}
    93  		}
    94  		c.Check(value, gc.Equals, expected)
    95  	}
    96  }
    97  
    98  func (ts configTestSpec) attrs() testing.Attrs {
    99  	return gce.ConfigAttrs.Merge(ts.insert).Delete(ts.remove...)
   100  }
   101  
   102  func (ts configTestSpec) newConfig(c *gc.C) *config.Config {
   103  	filename := ts.writeAuthFile(c)
   104  
   105  	attrs := ts.attrs()
   106  	if filename != "" {
   107  		attrs["auth-file"] = filename
   108  	}
   109  	cfg, err := testing.EnvironConfig(c).Apply(attrs)
   110  	c.Assert(err, jc.ErrorIsNil)
   111  	return cfg
   112  }
   113  
   114  func (ts configTestSpec) writeAuthFile(c *gc.C) string {
   115  	value, ok := ts.insert["auth-file"]
   116  	if !ok {
   117  		return ""
   118  	}
   119  	filename := value.(string)
   120  	if filename == "" {
   121  		return ""
   122  	}
   123  	filename = filepath.Join(ts.rootDir, filename)
   124  	err := os.MkdirAll(filepath.Dir(filename), 0755)
   125  	c.Assert(err, jc.ErrorIsNil)
   126  	err = ioutil.WriteFile(filename, []byte(gce.AuthFile), 0600)
   127  	c.Assert(err, jc.ErrorIsNil)
   128  	return filename
   129  }
   130  
   131  func (ts configTestSpec) fixCfg(c *gc.C, cfg *config.Config) *config.Config {
   132  	fixes := make(map[string]interface{})
   133  
   134  	var filename string
   135  	if value, ok := ts.insert["auth-file"]; ok {
   136  		filename = value.(string)
   137  		if filename != "" {
   138  			filename = filepath.Join(ts.rootDir, filename)
   139  		}
   140  	}
   141  
   142  	// Set changed values.
   143  	fixes = updateAttrs(fixes, ts.insert)
   144  	if filename != "" {
   145  		fixes = updateAttrs(fixes, testing.Attrs{"auth-file": filename})
   146  	}
   147  
   148  	newCfg, err := cfg.Apply(fixes)
   149  	c.Assert(err, jc.ErrorIsNil)
   150  	return newCfg
   151  }
   152  
   153  func updateAttrs(attrs, updates testing.Attrs) testing.Attrs {
   154  	updated := make(testing.Attrs, len(attrs))
   155  	for k, v := range attrs {
   156  		updated[k] = v
   157  	}
   158  	for k, v := range updates {
   159  		updated[k] = v
   160  	}
   161  	return updated
   162  }
   163  
   164  var newConfigTests = []configTestSpec{{
   165  	info:   "auth-file is optional",
   166  	remove: []string{"auth-file"},
   167  	expect: testing.Attrs{"auth-file": ""},
   168  }, {
   169  	info:   "auth-file can be empty",
   170  	insert: testing.Attrs{"auth-file": ""},
   171  	expect: testing.Attrs{"auth-file": ""},
   172  }, {
   173  	info: "auth-file ignored",
   174  	insert: testing.Attrs{
   175  		"auth-file":    "/home/someuser/gce.json",
   176  		"client-id":    "spam.x",
   177  		"client-email": "spam@x",
   178  		"private-key":  "abc",
   179  	},
   180  	expect: testing.Attrs{
   181  		"auth-file":    "/home/someuser/gce.json",
   182  		"client-id":    "spam.x",
   183  		"client-email": "spam@x",
   184  		"private-key":  "abc",
   185  	},
   186  }, {
   187  	info:   "auth-file parsed",
   188  	insert: testing.Attrs{"auth-file": "/home/someuser/gce.json"},
   189  	remove: []string{"client-id", "client-email", "private-key"},
   190  	expect: testing.Attrs{
   191  		"auth-file":    "/home/someuser/gce.json",
   192  		"client-id":    gce.ClientID,
   193  		"client-email": gce.ClientEmail,
   194  		"private-key":  gce.PrivateKey,
   195  	},
   196  }, {
   197  	info:   "client-id is required",
   198  	remove: []string{"client-id"},
   199  	err:    "client-id: expected string, got nothing",
   200  }, {
   201  	info:   "client-id cannot be empty",
   202  	insert: testing.Attrs{"client-id": ""},
   203  	err:    "client-id: must not be empty",
   204  }, {
   205  	info:   "private-key is required",
   206  	remove: []string{"private-key"},
   207  	err:    "private-key: expected string, got nothing",
   208  }, {
   209  	info:   "private-key cannot be empty",
   210  	insert: testing.Attrs{"private-key": ""},
   211  	err:    "private-key: must not be empty",
   212  }, {
   213  	info:   "client-email is required",
   214  	remove: []string{"client-email"},
   215  	err:    "client-email: expected string, got nothing",
   216  }, {
   217  	info:   "client-email cannot be empty",
   218  	insert: testing.Attrs{"client-email": ""},
   219  	err:    "client-email: must not be empty",
   220  }, {
   221  	info:   "region is optional",
   222  	remove: []string{"region"},
   223  	expect: testing.Attrs{"region": "us-central1"},
   224  }, {
   225  	info:   "region cannot be empty",
   226  	insert: testing.Attrs{"region": ""},
   227  	err:    "region: must not be empty",
   228  }, {
   229  	info:   "project-id is required",
   230  	remove: []string{"project-id"},
   231  	err:    "project-id: expected string, got nothing",
   232  }, {
   233  	info:   "project-id cannot be empty",
   234  	insert: testing.Attrs{"project-id": ""},
   235  	err:    "project-id: must not be empty",
   236  }, {
   237  	info:   "image-endpoint is inserted if missing",
   238  	remove: []string{"image-endpoint"},
   239  	expect: testing.Attrs{"image-endpoint": "https://www.googleapis.com"},
   240  }, {
   241  	info:   "image-endpoint cannot be empty",
   242  	insert: testing.Attrs{"image-endpoint": ""},
   243  	err:    "image-endpoint: must not be empty",
   244  }, {
   245  	info:   "unknown field is not touched",
   246  	insert: testing.Attrs{"unknown-field": 12345},
   247  	expect: testing.Attrs{"unknown-field": 12345},
   248  }}
   249  
   250  func (s *ConfigSuite) TestNewEnvironConfig(c *gc.C) {
   251  	for i, test := range newConfigTests {
   252  		c.Logf("test %d: %s", i, test.info)
   253  
   254  		test.rootDir = s.rootDir
   255  		testConfig := test.newConfig(c)
   256  		environ, err := environs.New(testConfig)
   257  
   258  		// Check the result
   259  		if test.err != "" {
   260  			test.checkFailure(c, err, "invalid config")
   261  		} else {
   262  			test.checkSuccess(c, environ, err)
   263  		}
   264  	}
   265  }
   266  
   267  // TODO(wwitzel3) refactor to provider_test file
   268  func (s *ConfigSuite) TestValidateNewConfig(c *gc.C) {
   269  	for i, test := range newConfigTests {
   270  		c.Logf("test %d: %s", i, test.info)
   271  
   272  		test.rootDir = s.rootDir
   273  		testConfig := test.newConfig(c)
   274  		validatedConfig, err := gce.Provider.Validate(testConfig, nil)
   275  
   276  		// Check the result
   277  		if test.err != "" {
   278  			test.checkFailure(c, err, "invalid config")
   279  		} else {
   280  			c.Check(validatedConfig, gc.NotNil)
   281  			test.checkSuccess(c, validatedConfig, err)
   282  		}
   283  	}
   284  }
   285  
   286  // TODO(wwitzel3) refactor to the provider_test file
   287  func (s *ConfigSuite) TestValidateOldConfig(c *gc.C) {
   288  	for i, test := range newConfigTests {
   289  		c.Logf("test %d: %s", i, test.info)
   290  
   291  		test.rootDir = s.rootDir
   292  		oldcfg := test.newConfig(c)
   293  		newcfg := test.fixCfg(c, s.config)
   294  		expected := updateAttrs(gce.ConfigAttrs, test.insert)
   295  
   296  		// Validate the new config (relative to the old one) using the
   297  		// provider.
   298  		validatedConfig, err := gce.Provider.Validate(newcfg, oldcfg)
   299  
   300  		// Check the result.
   301  		if test.err != "" {
   302  			test.checkFailure(c, err, "invalid base config")
   303  		} else {
   304  			if test.insert == nil && test.remove != nil {
   305  				// No defaults are set on the old config.
   306  				c.Check(err, gc.ErrorMatches, "invalid base config: .*")
   307  				continue
   308  			}
   309  
   310  			if !c.Check(err, jc.ErrorIsNil) {
   311  				continue
   312  			}
   313  			// We verify that Validate filled in the defaults
   314  			// appropriately.
   315  			c.Check(validatedConfig, gc.NotNil)
   316  			test.checkAttrs(c, expected, validatedConfig)
   317  		}
   318  	}
   319  }
   320  
   321  var changeConfigTests = []configTestSpec{{
   322  	info:   "no change, no error",
   323  	expect: gce.ConfigAttrs,
   324  }, {
   325  	info:   "cannot change auth-file",
   326  	insert: testing.Attrs{"auth-file": "gce.json"},
   327  	err:    "auth-file: cannot change from  to .*gce.json",
   328  }, {
   329  	info:   "cannot change private-key",
   330  	insert: testing.Attrs{"private-key": "okkult"},
   331  	err:    "private-key: cannot change from " + gce.PrivateKey + " to okkult",
   332  }, {
   333  	info:   "cannot change client-id",
   334  	insert: testing.Attrs{"client-id": "mutant"},
   335  	err:    "client-id: cannot change from " + gce.ClientID + " to mutant",
   336  }, {
   337  	info:   "cannot change client-email",
   338  	insert: testing.Attrs{"client-email": "spam@eggs.com"},
   339  	err:    "client-email: cannot change from " + gce.ClientEmail + " to spam@eggs.com",
   340  }, {
   341  	info:   "cannot change region",
   342  	insert: testing.Attrs{"region": "not home"},
   343  	err:    "region: cannot change from home to not home",
   344  }, {
   345  	info:   "cannot change project-id",
   346  	insert: testing.Attrs{"project-id": "your-juju"},
   347  	err:    "project-id: cannot change from my-juju to your-juju",
   348  }, {
   349  	info:   "can insert unknown field",
   350  	insert: testing.Attrs{"unknown": "ignoti"},
   351  	expect: testing.Attrs{"unknown": "ignoti"},
   352  }}
   353  
   354  // TODO(wwitzel3) refactor this to the provider_test file.
   355  func (s *ConfigSuite) TestValidateChange(c *gc.C) {
   356  	for i, test := range changeConfigTests {
   357  		c.Logf("test %d: %s", i, test.info)
   358  
   359  		test.rootDir = s.rootDir
   360  		testConfig := test.newConfig(c)
   361  		validatedConfig, err := gce.Provider.Validate(testConfig, s.config)
   362  
   363  		// Check the result.
   364  		if test.err != "" {
   365  			test.checkFailure(c, err, "invalid config change")
   366  		} else {
   367  			test.checkSuccess(c, validatedConfig, err)
   368  		}
   369  	}
   370  }
   371  
   372  func (s *ConfigSuite) TestSetConfig(c *gc.C) {
   373  	for i, test := range changeConfigTests {
   374  		c.Logf("test %d: %s", i, test.info)
   375  
   376  		environ, err := environs.New(s.config)
   377  		c.Assert(err, jc.ErrorIsNil)
   378  
   379  		test.rootDir = s.rootDir
   380  		testConfig := test.newConfig(c)
   381  		err = environ.SetConfig(testConfig)
   382  
   383  		// Check the result.
   384  		if test.err != "" {
   385  			test.checkFailure(c, err, "invalid config change")
   386  			test.checkAttrs(c, environ.Config().AllAttrs(), s.config)
   387  		} else {
   388  			test.checkSuccess(c, environ.Config(), err)
   389  		}
   390  	}
   391  }