github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/provider/openstack/config_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package openstack
     5  
     6  import (
     7  	"os"
     8  
     9  	jc "github.com/juju/testing/checkers"
    10  	gc "gopkg.in/check.v1"
    11  
    12  	"github.com/juju/juju/cloud"
    13  	"github.com/juju/juju/environs"
    14  	"github.com/juju/juju/environs/config"
    15  	"github.com/juju/juju/testing"
    16  )
    17  
    18  type ConfigSuite struct {
    19  	testing.BaseSuite
    20  	savedVars map[string]string
    21  }
    22  
    23  // Ensure any environment variables a user may have set locally are reset.
    24  var envVars = map[string]string{
    25  	"AWS_SECRET_ACCESS_KEY": "",
    26  	"EC2_SECRET_KEYS":       "",
    27  	"NOVA_API_KEY":          "",
    28  	"NOVA_PASSWORD":         "",
    29  	"NOVA_PROJECT_ID":       "",
    30  	"NOVA_REGION":           "",
    31  	"NOVA_USERNAME":         "",
    32  	"OS_ACCESS_KEY":         "",
    33  	"OS_AUTH_URL":           "",
    34  	"OS_PASSWORD":           "",
    35  	"OS_REGION_NAME":        "",
    36  	"OS_SECRET_KEY":         "",
    37  	"OS_TENANT_NAME":        "",
    38  	"OS_USERNAME":           "",
    39  }
    40  
    41  var _ = gc.Suite(&ConfigSuite{})
    42  
    43  // configTest specifies a config parsing test, checking that env when
    44  // parsed as the openstack section of a config file matches
    45  // baseConfigResult when mutated by the mutate function, or that the
    46  // parse matches the given error.
    47  type configTest struct {
    48  	summary                 string
    49  	config                  testing.Attrs
    50  	change                  map[string]interface{}
    51  	expect                  map[string]interface{}
    52  	envVars                 map[string]string
    53  	region                  string
    54  	useFloatingIP           bool
    55  	useDefaultSecurityGroup bool
    56  	network                 string
    57  	username                string
    58  	password                string
    59  	tenantName              string
    60  	authMode                AuthMode
    61  	authURL                 string
    62  	accessKey               string
    63  	secretKey               string
    64  	firewallMode            string
    65  	err                     string
    66  	sslHostnameVerification bool
    67  	sslHostnameSet          bool
    68  	blockStorageSource      string
    69  }
    70  
    71  var requiredConfig = testing.Attrs{
    72  	"region":      "configtest",
    73  	"auth-url":    "http://auth",
    74  	"username":    "user",
    75  	"password":    "pass",
    76  	"tenant-name": "tenant",
    77  }
    78  
    79  func restoreEnvVars(envVars map[string]string) {
    80  	for k, v := range envVars {
    81  		os.Setenv(k, v)
    82  	}
    83  }
    84  
    85  func (t configTest) check(c *gc.C) {
    86  	attrs := testing.FakeConfig().Merge(testing.Attrs{
    87  		"type": "openstack",
    88  	}).Merge(t.config)
    89  
    90  	cfg, err := config.New(config.NoDefaults, attrs)
    91  	c.Assert(err, jc.ErrorIsNil)
    92  
    93  	// Set environment variables if any.
    94  	savedVars := make(map[string]string)
    95  	if t.envVars != nil {
    96  		for k, v := range t.envVars {
    97  			savedVars[k] = os.Getenv(k)
    98  			os.Setenv(k, v)
    99  		}
   100  	}
   101  	defer restoreEnvVars(savedVars)
   102  
   103  	e, err := environs.New(cfg)
   104  	if t.change != nil {
   105  		c.Assert(err, jc.ErrorIsNil)
   106  
   107  		// Testing a change in configuration.
   108  		var old, changed, valid *config.Config
   109  		osenv := e.(*Environ)
   110  		old = osenv.ecfg().Config
   111  		changed, err = old.Apply(t.change)
   112  		c.Assert(err, jc.ErrorIsNil)
   113  
   114  		// Keep err for validation below.
   115  		valid, err = providerInstance.Validate(changed, old)
   116  		if err == nil {
   117  			err = osenv.SetConfig(valid)
   118  		}
   119  	}
   120  	if t.err != "" {
   121  		c.Check(err, gc.ErrorMatches, t.err)
   122  		return
   123  	}
   124  	c.Assert(err, jc.ErrorIsNil)
   125  
   126  	ecfg := e.(*Environ).ecfg()
   127  	c.Assert(ecfg.Name(), gc.Equals, "testenv")
   128  	if t.region != "" {
   129  		c.Assert(ecfg.region(), gc.Equals, t.region)
   130  	}
   131  	if t.authMode != "" {
   132  		c.Assert(ecfg.authMode(), gc.Equals, t.authMode)
   133  	}
   134  	if t.accessKey != "" {
   135  		c.Assert(ecfg.accessKey(), gc.Equals, t.accessKey)
   136  	}
   137  	if t.secretKey != "" {
   138  		c.Assert(ecfg.secretKey(), gc.Equals, t.secretKey)
   139  	}
   140  	if t.username != "" {
   141  		c.Assert(ecfg.username(), gc.Equals, t.username)
   142  		c.Assert(ecfg.password(), gc.Equals, t.password)
   143  		c.Assert(ecfg.tenantName(), gc.Equals, t.tenantName)
   144  		c.Assert(ecfg.authURL(), gc.Equals, t.authURL)
   145  		expected := map[string]string{
   146  			"username":    t.username,
   147  			"password":    t.password,
   148  			"tenant-name": t.tenantName,
   149  		}
   150  		c.Assert(err, jc.ErrorIsNil)
   151  		actual, err := e.Provider().SecretAttrs(ecfg.Config)
   152  		c.Assert(err, jc.ErrorIsNil)
   153  		c.Assert(expected, gc.DeepEquals, actual)
   154  	}
   155  	if t.firewallMode != "" {
   156  		c.Assert(ecfg.FirewallMode(), gc.Equals, t.firewallMode)
   157  	}
   158  	c.Assert(ecfg.useFloatingIP(), gc.Equals, t.useFloatingIP)
   159  	c.Assert(ecfg.useDefaultSecurityGroup(), gc.Equals, t.useDefaultSecurityGroup)
   160  	c.Assert(ecfg.network(), gc.Equals, t.network)
   161  	// Default should be true
   162  	expectedHostnameVerification := true
   163  	if t.sslHostnameSet {
   164  		expectedHostnameVerification = t.sslHostnameVerification
   165  	}
   166  	c.Assert(ecfg.SSLHostnameVerification(), gc.Equals, expectedHostnameVerification)
   167  	for name, expect := range t.expect {
   168  		actual, found := ecfg.UnknownAttrs()[name]
   169  		c.Check(found, jc.IsTrue)
   170  		c.Check(actual, gc.Equals, expect)
   171  	}
   172  	if t.blockStorageSource != "" {
   173  		storage, ok := ecfg.StorageDefaultBlockSource()
   174  		c.Assert(ok, jc.IsTrue)
   175  		c.Assert(storage, gc.Equals, t.blockStorageSource)
   176  	}
   177  }
   178  
   179  func (s *ConfigSuite) SetUpTest(c *gc.C) {
   180  	s.BaseSuite.SetUpTest(c)
   181  	s.savedVars = make(map[string]string)
   182  	for v, val := range envVars {
   183  		s.savedVars[v] = os.Getenv(v)
   184  		os.Setenv(v, val)
   185  	}
   186  	s.PatchValue(&authenticateClient, func(*Environ) error { return nil })
   187  }
   188  
   189  func (s *ConfigSuite) TearDownTest(c *gc.C) {
   190  	for k, v := range s.savedVars {
   191  		os.Setenv(k, v)
   192  	}
   193  	s.BaseSuite.TearDownTest(c)
   194  }
   195  
   196  var configTests = []configTest{
   197  	{
   198  		summary: "setting region",
   199  		config: requiredConfig.Merge(testing.Attrs{
   200  			"region": "testreg",
   201  		}),
   202  		region: "testreg",
   203  	}, {
   204  		summary: "setting region (2)",
   205  		config: requiredConfig.Merge(testing.Attrs{
   206  			"region": "configtest",
   207  		}),
   208  		region: "configtest",
   209  	}, {
   210  		summary: "changing region",
   211  		config:  requiredConfig,
   212  		change: testing.Attrs{
   213  			"region": "otherregion",
   214  		},
   215  		err: `cannot change region from "configtest" to "otherregion"`,
   216  	}, {
   217  		summary: "invalid region",
   218  		config: requiredConfig.Merge(testing.Attrs{
   219  			"region": 666,
   220  		}),
   221  		err: `.*expected string, got int\(666\)`,
   222  	}, {
   223  		summary: "missing region in model",
   224  		config:  requiredConfig.Delete("region"),
   225  		err:     "missing region not valid",
   226  	}, {
   227  		summary: "invalid username",
   228  		config: requiredConfig.Merge(testing.Attrs{
   229  			"username": 666,
   230  		}),
   231  		err: `.*expected string, got int\(666\)`,
   232  	}, {
   233  		summary: "missing username in model",
   234  		config:  requiredConfig.Delete("username"),
   235  		err:     "missing username not valid",
   236  	}, {
   237  		summary: "invalid password",
   238  		config: requiredConfig.Merge(testing.Attrs{
   239  			"password": 666,
   240  		}),
   241  		err: `.*expected string, got int\(666\)`,
   242  	}, {
   243  		summary: "missing password in model",
   244  		config:  requiredConfig.Delete("password"),
   245  		err:     "missing password not valid",
   246  	}, {
   247  		summary: "invalid tenant-name",
   248  		config: requiredConfig.Merge(testing.Attrs{
   249  			"tenant-name": 666,
   250  		}),
   251  		err: `.*expected string, got int\(666\)`,
   252  	}, {
   253  		summary: "missing tenant in model",
   254  		config:  requiredConfig.Delete("tenant-name"),
   255  		err:     "missing tenant-name not valid",
   256  	}, {
   257  		summary: "invalid auth-url type",
   258  		config: requiredConfig.Merge(testing.Attrs{
   259  			"auth-url": 666,
   260  		}),
   261  		err: `.*expected string, got int\(666\)`,
   262  	}, {
   263  		summary: "missing auth-url in model",
   264  		config:  requiredConfig.Delete("auth-url"),
   265  		err:     "missing auth-url not valid",
   266  	}, {
   267  		summary: "invalid authorization mode",
   268  		config: requiredConfig.Merge(testing.Attrs{
   269  			"auth-mode": "invalid-mode",
   270  		}),
   271  		err: `auth-mode: expected one of \[keypair legacy userpass\], got "invalid-mode"`,
   272  	}, {
   273  		summary: "keypair authorization mode",
   274  		config: requiredConfig.Merge(testing.Attrs{
   275  			"auth-mode":  "keypair",
   276  			"access-key": "MyAccessKey",
   277  			"secret-key": "MySecretKey",
   278  		}),
   279  		authMode:  "keypair",
   280  		accessKey: "MyAccessKey",
   281  		secretKey: "MySecretKey",
   282  	}, {
   283  		summary: "keypair authorization mode without access key",
   284  		config: requiredConfig.Merge(testing.Attrs{
   285  			"auth-mode":  "keypair",
   286  			"secret-key": "MySecretKey",
   287  		}),
   288  		err: "missing access-key not valid",
   289  	}, {
   290  		summary: "keypair authorization mode without secret key",
   291  		config: requiredConfig.Merge(testing.Attrs{
   292  			"auth-mode":  "keypair",
   293  			"access-key": "MyAccessKey",
   294  		}),
   295  		err: "missing secret-key not valid",
   296  	}, {
   297  		summary: "invalid auth-url format",
   298  		config: requiredConfig.Merge(testing.Attrs{
   299  			"auth-url": "invalid",
   300  		}),
   301  		err: `invalid auth-url value "invalid"`,
   302  	}, {
   303  		summary: "valid auth args",
   304  		config: requiredConfig.Merge(testing.Attrs{
   305  			"username":    "jujuer",
   306  			"password":    "open sesame",
   307  			"tenant-name": "juju tenant",
   308  			"auth-mode":   "legacy",
   309  			"auth-url":    "http://some.url/v2",
   310  		}),
   311  		username:   "jujuer",
   312  		password:   "open sesame",
   313  		tenantName: "juju tenant",
   314  		authURL:    "http://some.url/v2",
   315  		authMode:   AuthLegacy,
   316  	}, {
   317  		summary: "default use floating ip",
   318  		config:  requiredConfig,
   319  		// Do not use floating IP's by default.
   320  		useFloatingIP: false,
   321  	}, {
   322  		summary: "use floating ip",
   323  		config: requiredConfig.Merge(testing.Attrs{
   324  			"use-floating-ip": true,
   325  		}),
   326  		useFloatingIP: true,
   327  	}, {
   328  		summary: "default use default security group",
   329  		config:  requiredConfig,
   330  		// Do not use default security group by default.
   331  		useDefaultSecurityGroup: false,
   332  	}, {
   333  		summary: "use default security group",
   334  		config: requiredConfig.Merge(testing.Attrs{
   335  			"use-default-secgroup": true,
   336  		}),
   337  		useDefaultSecurityGroup: true,
   338  	}, {
   339  		summary: "admin-secret given",
   340  		config: requiredConfig.Merge(testing.Attrs{
   341  			"admin-secret": "Futumpsh",
   342  		}),
   343  	}, {
   344  		summary:      "default firewall-mode",
   345  		config:       requiredConfig,
   346  		firewallMode: config.FwInstance,
   347  	}, {
   348  		summary: "instance firewall-mode",
   349  		config: requiredConfig.Merge(testing.Attrs{
   350  			"firewall-mode": "instance",
   351  		}),
   352  		firewallMode: config.FwInstance,
   353  	}, {
   354  		summary: "global firewall-mode",
   355  		config: requiredConfig.Merge(testing.Attrs{
   356  			"firewall-mode": "global",
   357  		}),
   358  		firewallMode: config.FwGlobal,
   359  	}, {
   360  		summary: "none firewall-mode",
   361  		config: requiredConfig.Merge(testing.Attrs{
   362  			"firewall-mode": "none",
   363  		}),
   364  		firewallMode: config.FwNone,
   365  	}, {
   366  		config: requiredConfig.Merge(testing.Attrs{
   367  			"future": "hammerstein",
   368  		}),
   369  		expect: testing.Attrs{
   370  			"future": "hammerstein",
   371  		},
   372  	}, {
   373  		config: requiredConfig,
   374  		change: testing.Attrs{
   375  			"future": "hammerstein",
   376  		},
   377  		expect: testing.Attrs{
   378  			"future": "hammerstein",
   379  		},
   380  	}, {
   381  		config: requiredConfig,
   382  		change: testing.Attrs{
   383  			"ssl-hostname-verification": false,
   384  		},
   385  		sslHostnameVerification: false,
   386  		sslHostnameSet:          true,
   387  	}, {
   388  		config: requiredConfig,
   389  		change: testing.Attrs{
   390  			"ssl-hostname-verification": true,
   391  		},
   392  		sslHostnameVerification: true,
   393  		sslHostnameSet:          true,
   394  	}, {
   395  		summary: "default network",
   396  		config:  requiredConfig,
   397  		network: "",
   398  	}, {
   399  		summary: "network",
   400  		config: requiredConfig.Merge(testing.Attrs{
   401  			"network": "a-network-label",
   402  		}),
   403  		network: "a-network-label",
   404  	}, {
   405  		summary: "block storage specified",
   406  		config: requiredConfig.Merge(testing.Attrs{
   407  			"storage-default-block-source": "my-cinder",
   408  		}),
   409  		blockStorageSource: "my-cinder",
   410  	},
   411  }
   412  
   413  func (s *ConfigSuite) TestConfig(c *gc.C) {
   414  	for i, t := range configTests {
   415  		c.Logf("test %d: %s (%v)", i, t.summary, t.config)
   416  		t.check(c)
   417  	}
   418  }
   419  
   420  func (s *ConfigSuite) TestDeprecatedAttributesRemoved(c *gc.C) {
   421  	attrs := testing.FakeConfig().Merge(testing.Attrs{
   422  		"type":                  "openstack",
   423  		"default-image-id":      "id-1234",
   424  		"default-instance-type": "big",
   425  		"username":              "u",
   426  		"password":              "p",
   427  		"tenant-name":           "t",
   428  		"region":                "r",
   429  		"auth-url":              "http://auth",
   430  	})
   431  
   432  	cfg, err := config.New(config.NoDefaults, attrs)
   433  	c.Assert(err, jc.ErrorIsNil)
   434  	// Keep err for validation below.
   435  	valid, err := providerInstance.Validate(cfg, nil)
   436  	c.Assert(err, jc.ErrorIsNil)
   437  	// Check deprecated attributes removed.
   438  	allAttrs := valid.AllAttrs()
   439  	for _, attr := range []string{"default-image-id", "default-instance-type"} {
   440  		_, ok := allAttrs[attr]
   441  		c.Assert(ok, jc.IsFalse)
   442  	}
   443  }
   444  
   445  func (s *ConfigSuite) TestBootstrapConfigSetsDefaultBlockSource(c *gc.C) {
   446  	attrs := testing.FakeConfig().Merge(testing.Attrs{
   447  		"type": "openstack",
   448  	})
   449  	cfg, err := config.New(config.NoDefaults, attrs)
   450  	c.Assert(err, jc.ErrorIsNil)
   451  	_, ok := cfg.StorageDefaultBlockSource()
   452  	c.Assert(ok, jc.IsFalse)
   453  
   454  	cfg, err = providerInstance.BootstrapConfig(bootstrapConfigParams(cfg))
   455  	c.Assert(err, jc.ErrorIsNil)
   456  	source, ok := cfg.StorageDefaultBlockSource()
   457  	c.Assert(ok, jc.IsTrue)
   458  	c.Assert(source, gc.Equals, "cinder")
   459  }
   460  
   461  func bootstrapConfigParams(cfg *config.Config) environs.BootstrapConfigParams {
   462  	return environs.BootstrapConfigParams{
   463  		Config: cfg,
   464  		Credentials: cloud.NewCredential(cloud.UserPassAuthType, map[string]string{
   465  			"username":    "user",
   466  			"password":    "secret",
   467  			"tenant-name": "sometenant",
   468  		}),
   469  		CloudRegion:   "region",
   470  		CloudEndpoint: "http://auth",
   471  	}
   472  }
   473  
   474  func (*ConfigSuite) TestSchema(c *gc.C) {
   475  	fields := providerInstance.Schema()
   476  	// Check that all the fields defined in environs/config
   477  	// are in the returned schema.
   478  	globalFields, err := config.Schema(nil)
   479  	c.Assert(err, gc.IsNil)
   480  	for name, field := range globalFields {
   481  		c.Check(fields[name], jc.DeepEquals, field)
   482  	}
   483  }