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