github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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/environs"
    13  	"github.com/juju/juju/environs/config"
    14  	envtesting "github.com/juju/juju/environs/testing"
    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                  map[string]interface{}
    50  	change                  map[string]interface{}
    51  	expect                  map[string]interface{}
    52  	envVars                 map[string]string
    53  	region                  string
    54  	controlBucket           string
    55  	useFloatingIP           bool
    56  	useDefaultSecurityGroup bool
    57  	network                 string
    58  	username                string
    59  	password                string
    60  	tenantName              string
    61  	authMode                AuthMode
    62  	authURL                 string
    63  	accessKey               string
    64  	secretKey               string
    65  	firewallMode            string
    66  	err                     string
    67  	sslHostnameVerification bool
    68  	sslHostnameSet          bool
    69  	blockStorageSource      string
    70  }
    71  
    72  type attrs map[string]interface{}
    73  
    74  func restoreEnvVars(envVars map[string]string) {
    75  	for k, v := range envVars {
    76  		os.Setenv(k, v)
    77  	}
    78  }
    79  
    80  func (t configTest) check(c *gc.C) {
    81  	attrs := testing.FakeConfig().Merge(testing.Attrs{
    82  		"type":           "openstack",
    83  		"control-bucket": "x",
    84  	}).Merge(t.config)
    85  
    86  	cfg, err := config.New(config.NoDefaults, attrs)
    87  	c.Assert(err, jc.ErrorIsNil)
    88  
    89  	// Set environment variables if any.
    90  	savedVars := make(map[string]string)
    91  	if t.envVars != nil {
    92  		for k, v := range t.envVars {
    93  			savedVars[k] = os.Getenv(k)
    94  			os.Setenv(k, v)
    95  		}
    96  	}
    97  	defer restoreEnvVars(savedVars)
    98  
    99  	e, err := environs.New(cfg)
   100  	if t.change != nil {
   101  		c.Assert(err, jc.ErrorIsNil)
   102  
   103  		// Testing a change in configuration.
   104  		var old, changed, valid *config.Config
   105  		osenv := e.(*environ)
   106  		old = osenv.ecfg().Config
   107  		changed, err = old.Apply(t.change)
   108  		c.Assert(err, jc.ErrorIsNil)
   109  
   110  		// Keep err for validation below.
   111  		valid, err = providerInstance.Validate(changed, old)
   112  		if err == nil {
   113  			err = osenv.SetConfig(valid)
   114  		}
   115  	}
   116  	if t.err != "" {
   117  		c.Check(err, gc.ErrorMatches, t.err)
   118  		return
   119  	}
   120  	c.Assert(err, jc.ErrorIsNil)
   121  
   122  	ecfg := e.(*environ).ecfg()
   123  	c.Assert(ecfg.Name(), gc.Equals, "testenv")
   124  	c.Assert(ecfg.controlBucket(), gc.Equals, "x")
   125  	if t.region != "" {
   126  		c.Assert(ecfg.region(), gc.Equals, t.region)
   127  	}
   128  	if t.authMode != "" {
   129  		c.Assert(ecfg.authMode(), gc.Equals, t.authMode)
   130  	}
   131  	if t.accessKey != "" {
   132  		c.Assert(ecfg.accessKey(), gc.Equals, t.accessKey)
   133  	}
   134  	if t.secretKey != "" {
   135  		c.Assert(ecfg.secretKey(), gc.Equals, t.secretKey)
   136  	}
   137  	if t.username != "" {
   138  		c.Assert(ecfg.username(), gc.Equals, t.username)
   139  		c.Assert(ecfg.password(), gc.Equals, t.password)
   140  		c.Assert(ecfg.tenantName(), gc.Equals, t.tenantName)
   141  		c.Assert(ecfg.authURL(), gc.Equals, t.authURL)
   142  		expected := map[string]string{
   143  			"username":    t.username,
   144  			"password":    t.password,
   145  			"tenant-name": t.tenantName,
   146  		}
   147  		c.Assert(err, jc.ErrorIsNil)
   148  		actual, err := e.Provider().SecretAttrs(ecfg.Config)
   149  		c.Assert(err, jc.ErrorIsNil)
   150  		c.Assert(expected, gc.DeepEquals, actual)
   151  	}
   152  	if t.firewallMode != "" {
   153  		c.Assert(ecfg.FirewallMode(), gc.Equals, t.firewallMode)
   154  	}
   155  	c.Assert(ecfg.useFloatingIP(), gc.Equals, t.useFloatingIP)
   156  	c.Assert(ecfg.useDefaultSecurityGroup(), gc.Equals, t.useDefaultSecurityGroup)
   157  	c.Assert(ecfg.network(), gc.Equals, t.network)
   158  	// Default should be true
   159  	expectedHostnameVerification := true
   160  	if t.sslHostnameSet {
   161  		expectedHostnameVerification = t.sslHostnameVerification
   162  	}
   163  	c.Assert(ecfg.SSLHostnameVerification(), gc.Equals, expectedHostnameVerification)
   164  	for name, expect := range t.expect {
   165  		actual, found := ecfg.UnknownAttrs()[name]
   166  		c.Check(found, jc.IsTrue)
   167  		c.Check(actual, gc.Equals, expect)
   168  	}
   169  	expectedStorage := "cinder"
   170  	if t.blockStorageSource != "" {
   171  		expectedStorage = t.blockStorageSource
   172  	}
   173  	storage, ok := ecfg.StorageDefaultBlockSource()
   174  	c.Assert(ok, jc.IsTrue)
   175  	c.Assert(storage, gc.Equals, expectedStorage)
   176  }
   177  
   178  func (s *ConfigSuite) SetUpTest(c *gc.C) {
   179  	s.BaseSuite.SetUpTest(c)
   180  	s.savedVars = make(map[string]string)
   181  	for v, val := range envVars {
   182  		s.savedVars[v] = os.Getenv(v)
   183  		os.Setenv(v, val)
   184  	}
   185  	s.PatchValue(&authenticateClient, func(*environ) error { return nil })
   186  }
   187  
   188  func (s *ConfigSuite) TearDownTest(c *gc.C) {
   189  	for k, v := range s.savedVars {
   190  		os.Setenv(k, v)
   191  	}
   192  	s.BaseSuite.TearDownTest(c)
   193  }
   194  
   195  var configTests = []configTest{
   196  	{
   197  		summary: "setting region",
   198  		config: attrs{
   199  			"region": "testreg",
   200  		},
   201  		region: "testreg",
   202  	}, {
   203  		summary: "setting region (2)",
   204  		config: attrs{
   205  			"region": "configtest",
   206  		},
   207  		region: "configtest",
   208  	}, {
   209  		summary: "changing region",
   210  		config: attrs{
   211  			"region": "configtest",
   212  		},
   213  		change: attrs{
   214  			"region": "somereg",
   215  		},
   216  		err: `cannot change region from "configtest" to "somereg"`,
   217  	}, {
   218  		summary: "invalid region",
   219  		config: attrs{
   220  			"region": 666,
   221  		},
   222  		err: `.*expected string, got int\(666\)`,
   223  	}, {
   224  		summary: "missing region in environment",
   225  		envVars: map[string]string{
   226  			"OS_REGION_NAME": "",
   227  			"NOVA_REGION":    "",
   228  		},
   229  		err: "required environment variable not set for credentials attribute: Region",
   230  	}, {
   231  		summary: "invalid username",
   232  		config: attrs{
   233  			"username": 666,
   234  		},
   235  		err: `.*expected string, got int\(666\)`,
   236  	}, {
   237  		summary: "missing username in environment",
   238  		err:     "required environment variable not set for credentials attribute: User",
   239  		envVars: map[string]string{
   240  			"OS_USERNAME":   "",
   241  			"NOVA_USERNAME": "",
   242  		},
   243  	}, {
   244  		summary: "invalid password",
   245  		config: attrs{
   246  			"password": 666,
   247  		},
   248  		err: `.*expected string, got int\(666\)`,
   249  	}, {
   250  		summary: "missing password in environment",
   251  		err:     "required environment variable not set for credentials attribute: Secrets",
   252  		envVars: map[string]string{
   253  			"OS_PASSWORD":   "",
   254  			"NOVA_PASSWORD": "",
   255  		},
   256  	}, {
   257  		summary: "invalid tenant-name",
   258  		config: attrs{
   259  			"tenant-name": 666,
   260  		},
   261  		err: `.*expected string, got int\(666\)`,
   262  	}, {
   263  		summary: "missing tenant in environment",
   264  		err:     "required environment variable not set for credentials attribute: TenantName",
   265  		envVars: map[string]string{
   266  			"OS_TENANT_NAME":  "",
   267  			"NOVA_PROJECT_ID": "",
   268  		},
   269  	}, {
   270  		summary: "invalid auth-url type",
   271  		config: attrs{
   272  			"auth-url": 666,
   273  		},
   274  		err: `.*expected string, got int\(666\)`,
   275  	}, {
   276  		summary: "missing auth-url in environment",
   277  		err:     "required environment variable not set for credentials attribute: URL",
   278  		envVars: map[string]string{
   279  			"OS_AUTH_URL": "",
   280  		},
   281  	}, {
   282  		summary: "invalid authorization mode",
   283  		config: attrs{
   284  			"auth-mode": "invalid-mode",
   285  		},
   286  		err: `auth-mode: expected one of \[keypair legacy userpass\], got "invalid-mode"`,
   287  	}, {
   288  		summary: "keypair authorization mode",
   289  		config: attrs{
   290  			"auth-mode":  "keypair",
   291  			"access-key": "MyAccessKey",
   292  			"secret-key": "MySecretKey",
   293  		},
   294  		authMode:  "keypair",
   295  		accessKey: "MyAccessKey",
   296  		secretKey: "MySecretKey",
   297  	}, {
   298  		summary: "keypair authorization mode without access key",
   299  		config: attrs{
   300  			"auth-mode":  "keypair",
   301  			"secret-key": "MySecretKey",
   302  		},
   303  		envVars: map[string]string{
   304  			"OS_USERNAME": "",
   305  		},
   306  		err: "required environment variable not set for credentials attribute: User",
   307  	}, {
   308  		summary: "keypair authorization mode without secret key",
   309  		config: attrs{
   310  			"auth-mode":  "keypair",
   311  			"access-key": "MyAccessKey",
   312  		},
   313  		envVars: map[string]string{
   314  			"OS_PASSWORD": "",
   315  		},
   316  		err: "required environment variable not set for credentials attribute: Secrets",
   317  	}, {
   318  		summary: "invalid auth-url format",
   319  		config: attrs{
   320  			"auth-url": "invalid",
   321  		},
   322  		err: `invalid auth-url value "invalid"`,
   323  	}, {
   324  		summary: "invalid control-bucket",
   325  		config: attrs{
   326  			"control-bucket": 666,
   327  		},
   328  		err: `.*expected string, got int\(666\)`,
   329  	}, {
   330  		summary: "changing control-bucket",
   331  		change: attrs{
   332  			"control-bucket": "new-x",
   333  		},
   334  		err: `cannot change control-bucket from "x" to "new-x"`,
   335  	}, {
   336  		summary: "valid auth args",
   337  		config: attrs{
   338  			"username":    "jujuer",
   339  			"password":    "open sesame",
   340  			"tenant-name": "juju tenant",
   341  			"auth-mode":   "legacy",
   342  			"auth-url":    "http://some/url",
   343  		},
   344  		username:   "jujuer",
   345  		password:   "open sesame",
   346  		tenantName: "juju tenant",
   347  		authURL:    "http://some/url",
   348  		authMode:   AuthLegacy,
   349  	}, {
   350  		summary: "valid auth args in environment",
   351  		envVars: map[string]string{
   352  			"OS_USERNAME":    "jujuer",
   353  			"OS_PASSWORD":    "open sesame",
   354  			"OS_AUTH_URL":    "http://some/url",
   355  			"OS_TENANT_NAME": "juju tenant",
   356  			"OS_REGION_NAME": "region",
   357  		},
   358  		username:   "jujuer",
   359  		password:   "open sesame",
   360  		tenantName: "juju tenant",
   361  		authURL:    "http://some/url",
   362  		region:     "region",
   363  	}, {
   364  		summary:  "default auth mode based on environment",
   365  		authMode: AuthUserPass,
   366  	}, {
   367  		summary: "default use floating ip",
   368  		// Do not use floating IP's by default.
   369  		useFloatingIP: false,
   370  	}, {
   371  		summary: "use floating ip",
   372  		config: attrs{
   373  			"use-floating-ip": true,
   374  		},
   375  		useFloatingIP: true,
   376  	}, {
   377  		summary: "default use default security group",
   378  		// Do not use default security group by default.
   379  		useDefaultSecurityGroup: false,
   380  	}, {
   381  		summary: "use default security group",
   382  		config: attrs{
   383  			"use-default-secgroup": true,
   384  		},
   385  		useDefaultSecurityGroup: true,
   386  	}, {
   387  		summary: "admin-secret given",
   388  		config: attrs{
   389  			"admin-secret": "Futumpsh",
   390  		},
   391  	}, {
   392  		summary:      "default firewall-mode",
   393  		config:       attrs{},
   394  		firewallMode: config.FwInstance,
   395  	}, {
   396  		summary: "instance firewall-mode",
   397  		config: attrs{
   398  			"firewall-mode": "instance",
   399  		},
   400  		firewallMode: config.FwInstance,
   401  	}, {
   402  		summary: "global firewall-mode",
   403  		config: attrs{
   404  			"firewall-mode": "global",
   405  		},
   406  		firewallMode: config.FwGlobal,
   407  	}, {
   408  		summary: "none firewall-mode",
   409  		config: attrs{
   410  			"firewall-mode": "none",
   411  		},
   412  		firewallMode: config.FwNone,
   413  	}, {
   414  		config: attrs{
   415  			"future": "hammerstein",
   416  		},
   417  		expect: attrs{
   418  			"future": "hammerstein",
   419  		},
   420  	}, {
   421  		change: attrs{
   422  			"future": "hammerstein",
   423  		},
   424  		expect: attrs{
   425  			"future": "hammerstein",
   426  		},
   427  	}, {
   428  		change: attrs{
   429  			"ssl-hostname-verification": false,
   430  		},
   431  		sslHostnameVerification: false,
   432  		sslHostnameSet:          true,
   433  	}, {
   434  		change: attrs{
   435  			"ssl-hostname-verification": true,
   436  		},
   437  		sslHostnameVerification: true,
   438  		sslHostnameSet:          true,
   439  	}, {
   440  		summary: "default network",
   441  		network: "",
   442  	}, {
   443  		summary: "network",
   444  		config: attrs{
   445  			"network": "a-network-label",
   446  		},
   447  		network: "a-network-label",
   448  	}, {
   449  		summary:            "no default block storage specified",
   450  		config:             attrs{},
   451  		blockStorageSource: "cinder",
   452  	}, {
   453  		summary: "block storage specified",
   454  		config: attrs{
   455  			"storage-default-block-source": "my-cinder",
   456  		},
   457  		blockStorageSource: "my-cinder",
   458  	},
   459  }
   460  
   461  func (s *ConfigSuite) TestConfig(c *gc.C) {
   462  	s.setupEnvCredentials()
   463  	for i, t := range configTests {
   464  		c.Logf("test %d: %s (%v)", i, t.summary, t.config)
   465  		t.check(c)
   466  	}
   467  }
   468  
   469  func (s *ConfigSuite) TestDeprecatedAttributesRemoved(c *gc.C) {
   470  	s.setupEnvCredentials()
   471  	attrs := testing.FakeConfig().Merge(testing.Attrs{
   472  		"type":                  "openstack",
   473  		"control-bucket":        "x",
   474  		"default-image-id":      "id-1234",
   475  		"default-instance-type": "big",
   476  	})
   477  
   478  	cfg, err := config.New(config.NoDefaults, attrs)
   479  	c.Assert(err, jc.ErrorIsNil)
   480  	// Keep err for validation below.
   481  	valid, err := providerInstance.Validate(cfg, nil)
   482  	c.Assert(err, jc.ErrorIsNil)
   483  	// Check deprecated attributes removed.
   484  	allAttrs := valid.AllAttrs()
   485  	for _, attr := range []string{"default-image-id", "default-instance-type"} {
   486  		_, ok := allAttrs[attr]
   487  		c.Assert(ok, jc.IsFalse)
   488  	}
   489  }
   490  
   491  func (s *ConfigSuite) TestPrepareInsertsUniqueControlBucket(c *gc.C) {
   492  	s.setupEnvCredentials()
   493  	attrs := testing.FakeConfig().Merge(testing.Attrs{
   494  		"type": "openstack",
   495  	})
   496  	cfg, err := config.New(config.NoDefaults, attrs)
   497  	c.Assert(err, jc.ErrorIsNil)
   498  
   499  	ctx := envtesting.BootstrapContext(c)
   500  	env0, err := providerInstance.PrepareForBootstrap(ctx, cfg)
   501  	c.Assert(err, jc.ErrorIsNil)
   502  	bucket0 := env0.(*environ).ecfg().controlBucket()
   503  	c.Assert(bucket0, gc.Matches, "[a-f0-9]{32}")
   504  
   505  	env1, err := providerInstance.PrepareForBootstrap(ctx, cfg)
   506  	c.Assert(err, jc.ErrorIsNil)
   507  	bucket1 := env1.(*environ).ecfg().controlBucket()
   508  	c.Assert(bucket1, gc.Matches, "[a-f0-9]{32}")
   509  
   510  	c.Assert(bucket1, gc.Not(gc.Equals), bucket0)
   511  }
   512  
   513  func (s *ConfigSuite) TestPrepareDoesNotTouchExistingControlBucket(c *gc.C) {
   514  	s.setupEnvCredentials()
   515  	attrs := testing.FakeConfig().Merge(testing.Attrs{
   516  		"type":           "openstack",
   517  		"control-bucket": "burblefoo",
   518  	})
   519  	cfg, err := config.New(config.NoDefaults, attrs)
   520  	c.Assert(err, jc.ErrorIsNil)
   521  
   522  	env, err := providerInstance.PrepareForBootstrap(envtesting.BootstrapContext(c), cfg)
   523  	c.Assert(err, jc.ErrorIsNil)
   524  	bucket := env.(*environ).ecfg().controlBucket()
   525  	c.Assert(bucket, gc.Equals, "burblefoo")
   526  }
   527  
   528  func (s *ConfigSuite) TestPrepareSetsDefaultBlockSource(c *gc.C) {
   529  	s.setupEnvCredentials()
   530  	attrs := testing.FakeConfig().Merge(testing.Attrs{
   531  		"type": "openstack",
   532  	})
   533  	cfg, err := config.New(config.NoDefaults, attrs)
   534  	c.Assert(err, jc.ErrorIsNil)
   535  
   536  	env, err := providerInstance.PrepareForBootstrap(envtesting.BootstrapContext(c), cfg)
   537  	c.Assert(err, jc.ErrorIsNil)
   538  	source, ok := env.(*environ).ecfg().StorageDefaultBlockSource()
   539  	c.Assert(ok, jc.IsTrue)
   540  	c.Assert(source, gc.Equals, "cinder")
   541  }
   542  
   543  func (s *ConfigSuite) setupEnvCredentials() {
   544  	os.Setenv("OS_USERNAME", "user")
   545  	os.Setenv("OS_PASSWORD", "secret")
   546  	os.Setenv("OS_AUTH_URL", "http://auth")
   547  	os.Setenv("OS_TENANT_NAME", "sometenant")
   548  	os.Setenv("OS_REGION_NAME", "region")
   549  }
   550  
   551  func (*ConfigSuite) TestSchema(c *gc.C) {
   552  	fields := providerInstance.Schema()
   553  	// Check that all the fields defined in environs/config
   554  	// are in the returned schema.
   555  	globalFields, err := config.Schema(nil)
   556  	c.Assert(err, gc.IsNil)
   557  	for name, field := range globalFields {
   558  		c.Check(fields[name], jc.DeepEquals, field)
   559  	}
   560  }