launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/environs/config/config_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package config_test
     5  
     6  import (
     7  	"fmt"
     8  	"regexp"
     9  	stdtesting "testing"
    10  	"time"
    11  
    12  	"github.com/loggo/loggo"
    13  	gc "launchpad.net/gocheck"
    14  
    15  	"launchpad.net/juju-core/cert"
    16  	"launchpad.net/juju-core/environs/config"
    17  	"launchpad.net/juju-core/juju/osenv"
    18  	"launchpad.net/juju-core/schema"
    19  	"launchpad.net/juju-core/testing"
    20  	jc "launchpad.net/juju-core/testing/checkers"
    21  	"launchpad.net/juju-core/testing/testbase"
    22  	"launchpad.net/juju-core/version"
    23  )
    24  
    25  func Test(t *stdtesting.T) {
    26  	gc.TestingT(t)
    27  }
    28  
    29  type ConfigSuite struct {
    30  	testbase.LoggingSuite
    31  	home string
    32  }
    33  
    34  var _ = gc.Suite(&ConfigSuite{})
    35  
    36  func (s *ConfigSuite) SetUpTest(c *gc.C) {
    37  	s.LoggingSuite.SetUpTest(c)
    38  	// Make sure that the defaults are used, which
    39  	// is <root>=WARNING
    40  	loggo.ResetLoggers()
    41  }
    42  
    43  // sampleConfig holds a configuration with all required
    44  // attributes set.
    45  var sampleConfig = testing.Attrs{
    46  	"type":                      "my-type",
    47  	"name":                      "my-name",
    48  	"authorized-keys":           testing.FakeAuthKeys,
    49  	"firewall-mode":             config.FwInstance,
    50  	"admin-secret":              "foo",
    51  	"unknown":                   "my-unknown",
    52  	"ca-cert":                   caCert,
    53  	"ssl-hostname-verification": true,
    54  	"development":               false,
    55  	"state-port":                1234,
    56  	"api-port":                  4321,
    57  	"syslog-port":               2345,
    58  	"default-series":            "precise",
    59  }
    60  
    61  type configTest struct {
    62  	about       string
    63  	useDefaults config.Defaulting
    64  	attrs       map[string]interface{}
    65  	err         string
    66  }
    67  
    68  var configTests = []configTest{
    69  	{
    70  		about:       "The minimum good configuration",
    71  		useDefaults: config.UseDefaults,
    72  		attrs: testing.Attrs{
    73  			"type": "my-type",
    74  			"name": "my-name",
    75  		},
    76  	}, {
    77  		about:       "Metadata URLs",
    78  		useDefaults: config.UseDefaults,
    79  		attrs: testing.Attrs{
    80  			"type":               "my-type",
    81  			"name":               "my-name",
    82  			"image-metadata-url": "image-url",
    83  			"tools-metadata-url": "tools-metadata-url-value",
    84  		},
    85  	}, {
    86  		about:       "Deprecated tools metadata URL used",
    87  		useDefaults: config.UseDefaults,
    88  		attrs: testing.Attrs{
    89  			"type":      "my-type",
    90  			"name":      "my-name",
    91  			"tools-url": "tools-metadata-url-value",
    92  		},
    93  	}, {
    94  		about:       "Deprecated tools metadata URL ignored",
    95  		useDefaults: config.UseDefaults,
    96  		attrs: testing.Attrs{
    97  			"type":               "my-type",
    98  			"name":               "my-name",
    99  			"tools-metadata-url": "tools-metadata-url-value",
   100  			"tools-url":          "ignore-me",
   101  		},
   102  	}, {
   103  		about:       "Explicit series",
   104  		useDefaults: config.UseDefaults,
   105  		attrs: testing.Attrs{
   106  			"type":           "my-type",
   107  			"name":           "my-name",
   108  			"default-series": "my-series",
   109  		},
   110  	}, {
   111  		about:       "Implicit series with empty value",
   112  		useDefaults: config.UseDefaults,
   113  		attrs: testing.Attrs{
   114  			"type":           "my-type",
   115  			"name":           "my-name",
   116  			"default-series": "",
   117  		},
   118  	}, {
   119  		about:       "Explicit logging",
   120  		useDefaults: config.UseDefaults,
   121  		attrs: testing.Attrs{
   122  			"type":           "my-type",
   123  			"name":           "my-name",
   124  			"logging-config": "juju=INFO",
   125  		},
   126  	}, {
   127  		about:       "Explicit authorized-keys",
   128  		useDefaults: config.UseDefaults,
   129  		attrs: testing.Attrs{
   130  			"type":            "my-type",
   131  			"name":            "my-name",
   132  			"authorized-keys": testing.FakeAuthKeys,
   133  		},
   134  	}, {
   135  		about:       "Load authorized-keys from path",
   136  		useDefaults: config.UseDefaults,
   137  		attrs: testing.Attrs{
   138  			"type":                 "my-type",
   139  			"name":                 "my-name",
   140  			"authorized-keys-path": "~/.ssh/authorized_keys2",
   141  		},
   142  	}, {
   143  		about:       "CA cert & key from path",
   144  		useDefaults: config.UseDefaults,
   145  		attrs: testing.Attrs{
   146  			"type":                "my-type",
   147  			"name":                "my-name",
   148  			"ca-cert-path":        "cacert2.pem",
   149  			"ca-private-key-path": "cakey2.pem",
   150  		},
   151  	}, {
   152  		about:       "CA cert & key from path; cert attribute set too",
   153  		useDefaults: config.UseDefaults,
   154  		attrs: testing.Attrs{
   155  			"type":                "my-type",
   156  			"name":                "my-name",
   157  			"ca-cert-path":        "cacert2.pem",
   158  			"ca-cert":             "ignored",
   159  			"ca-private-key-path": "cakey2.pem",
   160  		},
   161  	}, {
   162  		about:       "CA cert & key from ~ path",
   163  		useDefaults: config.UseDefaults,
   164  		attrs: testing.Attrs{
   165  			"type":                "my-type",
   166  			"name":                "my-name",
   167  			"ca-cert-path":        "~/othercert.pem",
   168  			"ca-private-key-path": "~/otherkey.pem",
   169  		},
   170  	}, /* {
   171  		about: "CA cert only from ~ path",
   172  		useDefaults: config.UseDefaults,
   173  		attrs: testing.Attrs{
   174  			"type":           "my-type",
   175  			"name":           "my-name",
   176  			"ca-cert-path":   "~/othercert.pem",
   177  			"ca-private-key": "",
   178  		},
   179  	}, {
   180  		about: "CA cert only as attribute",
   181  		useDefaults: config.UseDefaults,
   182  		attrs: testing.Attrs{
   183  			"type":           "my-type",
   184  			"name":           "my-name",
   185  			"ca-cert":        caCert,
   186  			"ca-private-key": "",
   187  		},
   188  	}, */{
   189  		about:       "CA cert and key as attributes",
   190  		useDefaults: config.UseDefaults,
   191  		attrs: testing.Attrs{
   192  			"type":           "my-type",
   193  			"name":           "my-name",
   194  			"ca-cert":        caCert,
   195  			"ca-private-key": caKey,
   196  		},
   197  	}, {
   198  		about:       "Mismatched CA cert and key",
   199  		useDefaults: config.UseDefaults,
   200  		attrs: testing.Attrs{
   201  			"type":           "my-type",
   202  			"name":           "my-name",
   203  			"ca-cert":        caCert,
   204  			"ca-private-key": caKey2,
   205  		},
   206  		err: "bad CA certificate/key in configuration: crypto/tls: private key does not match public key",
   207  	}, {
   208  		about:       "Invalid CA cert",
   209  		useDefaults: config.UseDefaults,
   210  		attrs: testing.Attrs{
   211  			"type":    "my-type",
   212  			"name":    "my-name",
   213  			"ca-cert": invalidCACert,
   214  		},
   215  		err: `bad CA certificate/key in configuration: (asn1:|ASN\.1) syntax error:.*`,
   216  	}, {
   217  		about:       "Invalid CA key",
   218  		useDefaults: config.UseDefaults,
   219  		attrs: testing.Attrs{
   220  			"type":           "my-type",
   221  			"name":           "my-name",
   222  			"ca-cert":        caCert,
   223  			"ca-private-key": invalidCAKey,
   224  		},
   225  		err: "bad CA certificate/key in configuration: crypto/tls:.*",
   226  	}, /* {
   227  		about: "No CA cert or key",
   228  		useDefaults: config.UseDefaults,
   229  		attrs: testing.Attrs{
   230  			"type":           "my-type",
   231  			"name":           "my-name",
   232  			"ca-cert":        "",
   233  			"ca-private-key": "",
   234  		},
   235  	}, {
   236  		about: "CA key but no cert",
   237  		useDefaults: config.UseDefaults,
   238  		attrs: testing.Attrs{
   239  			"type":           "my-type",
   240  			"name":           "my-name",
   241  			"ca-cert":        "",
   242  			"ca-private-key": caKey,
   243  		},
   244  		err: "bad CA certificate/key in configuration: crypto/tls:.*",
   245  	}, {
   246  		about: "No CA key",
   247  		useDefaults: config.UseDefaults,
   248  		attrs: testing.Attrs{
   249  			"type":           "my-type",
   250  			"name":           "my-name",
   251  			"ca-cert":        "foo",
   252  			"ca-private-key": "",
   253  		},
   254  		err: "bad CA certificate/key in configuration: no certificates found",
   255  	}, */{
   256  		about:       "CA cert specified as non-existent file",
   257  		useDefaults: config.UseDefaults,
   258  		attrs: testing.Attrs{
   259  			"type":         "my-type",
   260  			"name":         "my-name",
   261  			"ca-cert-path": "no-such-file",
   262  		},
   263  		err: `open .*\.juju/no-such-file: .*`,
   264  	}, {
   265  		about:       "CA key specified as non-existent file",
   266  		useDefaults: config.UseDefaults,
   267  		attrs: testing.Attrs{
   268  			"type":                "my-type",
   269  			"name":                "my-name",
   270  			"ca-private-key-path": "no-such-file",
   271  		},
   272  		err: `open .*\.juju/no-such-file: .*`,
   273  	}, {
   274  		about:       "Specified agent version",
   275  		useDefaults: config.UseDefaults,
   276  		attrs: testing.Attrs{
   277  			"type":            "my-type",
   278  			"name":            "my-name",
   279  			"authorized-keys": testing.FakeAuthKeys,
   280  			"agent-version":   "1.2.3",
   281  		},
   282  	}, {
   283  		about:       "Specified development flag",
   284  		useDefaults: config.UseDefaults,
   285  		attrs: testing.Attrs{
   286  			"type":            "my-type",
   287  			"name":            "my-name",
   288  			"authorized-keys": testing.FakeAuthKeys,
   289  			"development":     true,
   290  		},
   291  	}, {
   292  		about:       "Specified admin secret",
   293  		useDefaults: config.UseDefaults,
   294  		attrs: testing.Attrs{
   295  			"type":            "my-type",
   296  			"name":            "my-name",
   297  			"authorized-keys": testing.FakeAuthKeys,
   298  			"development":     false,
   299  			"admin-secret":    "pork",
   300  		},
   301  	}, {
   302  		about:       "Invalid development flag",
   303  		useDefaults: config.UseDefaults,
   304  		attrs: testing.Attrs{
   305  			"type":            "my-type",
   306  			"name":            "my-name",
   307  			"authorized-keys": testing.FakeAuthKeys,
   308  			"development":     "invalid",
   309  		},
   310  		err: `development: expected bool, got string\("invalid"\)`,
   311  	}, {
   312  		about:       "Invalid agent version",
   313  		useDefaults: config.UseDefaults,
   314  		attrs: testing.Attrs{
   315  			"type":            "my-type",
   316  			"name":            "my-name",
   317  			"authorized-keys": testing.FakeAuthKeys,
   318  			"agent-version":   "2",
   319  		},
   320  		err: `invalid agent version in environment configuration: "2"`,
   321  	}, {
   322  		about:       "Missing type",
   323  		useDefaults: config.UseDefaults,
   324  		attrs: testing.Attrs{
   325  			"name": "my-name",
   326  		},
   327  		err: "type: expected string, got nothing",
   328  	}, {
   329  		about:       "Empty type",
   330  		useDefaults: config.UseDefaults,
   331  		attrs: testing.Attrs{
   332  			"name": "my-name",
   333  			"type": "",
   334  		},
   335  		err: "empty type in environment configuration",
   336  	}, {
   337  		about:       "Missing name",
   338  		useDefaults: config.UseDefaults,
   339  		attrs: testing.Attrs{
   340  			"type": "my-type",
   341  		},
   342  		err: "name: expected string, got nothing",
   343  	}, {
   344  		about:       "Bad name, no slash",
   345  		useDefaults: config.UseDefaults,
   346  		attrs: testing.Attrs{
   347  			"name": "foo/bar",
   348  			"type": "my-type",
   349  		},
   350  		err: "environment name contains unsafe characters",
   351  	}, {
   352  		about:       "Bad name, no backslash",
   353  		useDefaults: config.UseDefaults,
   354  		attrs: testing.Attrs{
   355  			"name": "foo\\bar",
   356  			"type": "my-type",
   357  		},
   358  		err: "environment name contains unsafe characters",
   359  	}, {
   360  		about:       "Empty name",
   361  		useDefaults: config.UseDefaults,
   362  		attrs: testing.Attrs{
   363  			"type": "my-type",
   364  			"name": "",
   365  		},
   366  		err: "empty name in environment configuration",
   367  	}, {
   368  		about:       "Default firewall mode",
   369  		useDefaults: config.UseDefaults,
   370  		attrs: testing.Attrs{
   371  			"type": "my-type",
   372  			"name": "my-name",
   373  		},
   374  	}, {
   375  		about:       "Empty firewall mode",
   376  		useDefaults: config.UseDefaults,
   377  		attrs: testing.Attrs{
   378  			"type":          "my-type",
   379  			"name":          "my-name",
   380  			"firewall-mode": "",
   381  		},
   382  	}, {
   383  		about:       "Instance firewall mode",
   384  		useDefaults: config.UseDefaults,
   385  		attrs: testing.Attrs{
   386  			"type":          "my-type",
   387  			"name":          "my-name",
   388  			"firewall-mode": config.FwInstance,
   389  		},
   390  	}, {
   391  		about:       "Global firewall mode",
   392  		useDefaults: config.UseDefaults,
   393  		attrs: testing.Attrs{
   394  			"type":          "my-type",
   395  			"name":          "my-name",
   396  			"firewall-mode": config.FwGlobal,
   397  		},
   398  	}, {
   399  		about:       "Illegal firewall mode",
   400  		useDefaults: config.UseDefaults,
   401  		attrs: testing.Attrs{
   402  			"type":          "my-type",
   403  			"name":          "my-name",
   404  			"firewall-mode": "illegal",
   405  		},
   406  		err: "invalid firewall mode in environment configuration: .*",
   407  	}, {
   408  		about:       "ssl-hostname-verification off",
   409  		useDefaults: config.UseDefaults,
   410  		attrs: testing.Attrs{
   411  			"type": "my-type",
   412  			"name": "my-name",
   413  			"ssl-hostname-verification": false,
   414  		},
   415  	}, {
   416  		about:       "ssl-hostname-verification incorrect",
   417  		useDefaults: config.UseDefaults,
   418  		attrs: testing.Attrs{
   419  			"type": "my-type",
   420  			"name": "my-name",
   421  			"ssl-hostname-verification": "yes please",
   422  		},
   423  		err: `ssl-hostname-verification: expected bool, got string\("yes please"\)`,
   424  	}, {
   425  		about:       "provisioner-safe-mode off",
   426  		useDefaults: config.UseDefaults,
   427  		attrs: testing.Attrs{
   428  			"type":                  "my-type",
   429  			"name":                  "my-name",
   430  			"provisioner-safe-mode": false,
   431  		},
   432  	}, {
   433  		about:       "provisioner-safe-mode on",
   434  		useDefaults: config.UseDefaults,
   435  		attrs: testing.Attrs{
   436  			"type":                  "my-type",
   437  			"name":                  "my-name",
   438  			"provisioner-safe-mode": true,
   439  		},
   440  	}, {
   441  		about:       "provisioner-safe-mode incorrect",
   442  		useDefaults: config.UseDefaults,
   443  		attrs: testing.Attrs{
   444  			"type":                  "my-type",
   445  			"name":                  "my-name",
   446  			"provisioner-safe-mode": "yes please",
   447  		},
   448  		err: `provisioner-safe-mode: expected bool, got string\("yes please"\)`,
   449  	}, {
   450  		about:       "default image stream",
   451  		useDefaults: config.UseDefaults,
   452  		attrs: testing.Attrs{
   453  			"type": "my-type",
   454  			"name": "my-name",
   455  		},
   456  	}, {
   457  		about:       "explicit image stream",
   458  		useDefaults: config.UseDefaults,
   459  		attrs: testing.Attrs{
   460  			"type":         "my-type",
   461  			"name":         "my-name",
   462  			"image-stream": "daily",
   463  		},
   464  	}, {
   465  		about:       "Explicit state port",
   466  		useDefaults: config.UseDefaults,
   467  		attrs: testing.Attrs{
   468  			"type":       "my-type",
   469  			"name":       "my-name",
   470  			"state-port": 37042,
   471  		},
   472  	}, {
   473  		about:       "Invalid state port",
   474  		useDefaults: config.UseDefaults,
   475  		attrs: testing.Attrs{
   476  			"type":       "my-type",
   477  			"name":       "my-name",
   478  			"state-port": "illegal",
   479  		},
   480  		err: `state-port: expected number, got string\("illegal"\)`,
   481  	}, {
   482  		about:       "Explicit API port",
   483  		useDefaults: config.UseDefaults,
   484  		attrs: testing.Attrs{
   485  			"type":     "my-type",
   486  			"name":     "my-name",
   487  			"api-port": 77042,
   488  		},
   489  	}, {
   490  		about:       "Invalid API port",
   491  		useDefaults: config.UseDefaults,
   492  		attrs: testing.Attrs{
   493  			"type":     "my-type",
   494  			"name":     "my-name",
   495  			"api-port": "illegal",
   496  		},
   497  		err: `api-port: expected number, got string\("illegal"\)`,
   498  	}, {
   499  		about:       "Explicit syslog port",
   500  		useDefaults: config.UseDefaults,
   501  		attrs: testing.Attrs{
   502  			"type":        "my-type",
   503  			"name":        "my-name",
   504  			"syslog-port": 3456,
   505  		},
   506  	}, {
   507  		about:       "Invalid syslog port",
   508  		useDefaults: config.UseDefaults,
   509  		attrs: testing.Attrs{
   510  			"type":        "my-type",
   511  			"name":        "my-name",
   512  			"syslog-port": "illegal",
   513  		},
   514  		err: `syslog-port: expected number, got string\("illegal"\)`,
   515  	}, {
   516  		about:       "Explicit bootstrap timeout",
   517  		useDefaults: config.UseDefaults,
   518  		attrs: testing.Attrs{
   519  			"type":              "my-type",
   520  			"name":              "my-name",
   521  			"bootstrap-timeout": 300,
   522  		},
   523  	}, {
   524  		about:       "Invalid bootstrap timeout",
   525  		useDefaults: config.UseDefaults,
   526  		attrs: testing.Attrs{
   527  			"type":              "my-type",
   528  			"name":              "my-name",
   529  			"bootstrap-timeout": "illegal",
   530  		},
   531  		err: `bootstrap-timeout: expected number, got string\("illegal"\)`,
   532  	}, {
   533  		about:       "Explicit bootstrap retry delay",
   534  		useDefaults: config.UseDefaults,
   535  		attrs: testing.Attrs{
   536  			"type":                  "my-type",
   537  			"name":                  "my-name",
   538  			"bootstrap-retry-delay": 5,
   539  		},
   540  	}, {
   541  		about:       "Invalid bootstrap retry delay",
   542  		useDefaults: config.UseDefaults,
   543  		attrs: testing.Attrs{
   544  			"type":                  "my-type",
   545  			"name":                  "my-name",
   546  			"bootstrap-retry-delay": "illegal",
   547  		},
   548  		err: `bootstrap-retry-delay: expected number, got string\("illegal"\)`,
   549  	}, {
   550  		about:       "Explicit bootstrap addresses delay",
   551  		useDefaults: config.UseDefaults,
   552  		attrs: testing.Attrs{
   553  			"type": "my-type",
   554  			"name": "my-name",
   555  			"bootstrap-addresses-delay": 15,
   556  		},
   557  	}, {
   558  		about:       "Invalid bootstrap addresses delay",
   559  		useDefaults: config.UseDefaults,
   560  		attrs: testing.Attrs{
   561  			"type": "my-type",
   562  			"name": "my-name",
   563  			"bootstrap-addresses-delay": "illegal",
   564  		},
   565  		err: `bootstrap-addresses-delay: expected number, got string\("illegal"\)`,
   566  	}, {
   567  		about:       "Invalid logging configuration",
   568  		useDefaults: config.UseDefaults,
   569  		attrs: testing.Attrs{
   570  			"type":           "my-type",
   571  			"name":           "my-name",
   572  			"logging-config": "foo=bar",
   573  		},
   574  		err: `unknown severity level "bar"`,
   575  	}, {
   576  		about:       "Sample configuration",
   577  		useDefaults: config.UseDefaults,
   578  		attrs:       sampleConfig,
   579  	}, {
   580  		about:       "No defaults: sample configuration",
   581  		useDefaults: config.NoDefaults,
   582  		attrs:       sampleConfig,
   583  	}, {
   584  		about:       "No defaults: with ca-cert-path",
   585  		useDefaults: config.NoDefaults,
   586  		attrs:       sampleConfig.Merge(testing.Attrs{"ca-cert-path": "arble"}),
   587  		err:         `attribute "ca-cert-path" is not allowed in configuration`,
   588  	}, {
   589  		about:       "No defaults: with ca-private-key-path",
   590  		useDefaults: config.NoDefaults,
   591  		attrs:       sampleConfig.Merge(testing.Attrs{"ca-private-key-path": "arble"}),
   592  		err:         `attribute "ca-private-key-path" is not allowed in configuration`,
   593  	}, {
   594  		about:       "No defaults: with authorized-keys-path",
   595  		useDefaults: config.NoDefaults,
   596  		attrs:       sampleConfig.Merge(testing.Attrs{"authorized-keys-path": "arble"}),
   597  		err:         `attribute "authorized-keys-path" is not allowed in configuration`,
   598  	}, {
   599  		about:       "No defaults: missing authorized-keys",
   600  		useDefaults: config.NoDefaults,
   601  		attrs:       sampleConfig.Delete("authorized-keys"),
   602  		err:         `authorized-keys missing from environment configuration`,
   603  	}, {
   604  		about:       "Config settings from juju 1.13.3 actual installation",
   605  		useDefaults: config.NoDefaults,
   606  		attrs: map[string]interface{}{
   607  			"name":                      "sample",
   608  			"development":               false,
   609  			"admin-secret":              "",
   610  			"ssl-hostname-verification": true,
   611  			"authorized-keys":           "ssh-rsa mykeys rog@rog-x220\n",
   612  			"control-bucket":            "rog-some-control-bucket",
   613  			"region":                    "us-east-1",
   614  			"image-metadata-url":        "",
   615  			"ca-private-key":            "",
   616  			"default-series":            "precise",
   617  			"tools-metadata-url":        "",
   618  			"secret-key":                "a-secret-key",
   619  			"access-key":                "an-access-key",
   620  			"agent-version":             "1.13.2",
   621  			"ca-cert":                   caCert,
   622  			"firewall-mode":             "instance",
   623  			"type":                      "ec2",
   624  		},
   625  	}, {
   626  		about:       "Provider type null is replaced with manual",
   627  		useDefaults: config.UseDefaults,
   628  		attrs: testing.Attrs{
   629  			"type": "null",
   630  			"name": "my-name",
   631  		},
   632  	},
   633  	authTokenConfigTest("token=value, tokensecret=value", true),
   634  	authTokenConfigTest("token=value, ", true),
   635  	authTokenConfigTest("token=value, \ttokensecret=value", true),
   636  	authTokenConfigTest("", true),
   637  	authTokenConfigTest("token=value, tokensecret=value, \t", true),
   638  	authTokenConfigTest("=", false),
   639  	authTokenConfigTest("tokenvalue", false),
   640  	authTokenConfigTest("token=value, sometoken=", false),
   641  	authTokenConfigTest("token==value", false),
   642  	authTokenConfigTest(" token=value", false),
   643  	authTokenConfigTest("=value", false),
   644  	authTokenConfigTest("token=value, =z", false),
   645  	authTokenConfigTest("token=value =z", false),
   646  	authTokenConfigTest("\t", false),
   647  	missingAttributeNoDefault("default-series"),
   648  	missingAttributeNoDefault("firewall-mode"),
   649  	missingAttributeNoDefault("development"),
   650  	missingAttributeNoDefault("ssl-hostname-verification"),
   651  	// TODO(rog) reinstate these tests when we can lose
   652  	// backward compatibility with pre-1.13 config.
   653  	// missingAttributeNoDefault("state-port"),
   654  	// missingAttributeNoDefault("api-port"),
   655  }
   656  
   657  // authTokenConfigTest returns a config test that checks
   658  // that a configuration with the given auth token
   659  // will pass or fail, depending on the value of ok.
   660  func authTokenConfigTest(token string, ok bool) configTest {
   661  	var testName string
   662  	var err string
   663  
   664  	if ok {
   665  		testName = fmt.Sprintf("Valid auth token test: %q", token)
   666  	} else {
   667  		testName = fmt.Sprintf("Invalid auth token test: %q", token)
   668  		err = fmt.Sprintf("charm store auth token needs to be a set of key-value pairs, not %q", token)
   669  	}
   670  
   671  	return configTest{
   672  		about:       testName,
   673  		useDefaults: config.UseDefaults,
   674  		attrs:       sampleConfig.Merge(testing.Attrs{"charm-store-auth": token}),
   675  		err:         regexp.QuoteMeta(err),
   676  	}
   677  }
   678  
   679  func missingAttributeNoDefault(attrName string) configTest {
   680  	return configTest{
   681  		about:       fmt.Sprintf("No default: missing %s", attrName),
   682  		useDefaults: config.NoDefaults,
   683  		attrs:       sampleConfig.Delete(attrName),
   684  		err:         fmt.Sprintf("%s: expected [a-z]+, got nothing", attrName),
   685  	}
   686  }
   687  
   688  type testFile struct {
   689  	name, data string
   690  }
   691  
   692  func (*ConfigSuite) TestConfig(c *gc.C) {
   693  	files := []testing.TestFile{
   694  		{".ssh/id_dsa.pub", "dsa"},
   695  		{".ssh/id_rsa.pub", "rsa\n"},
   696  		{".ssh/identity.pub", "identity"},
   697  		{".ssh/authorized_keys", "auth0\n# first\nauth1\n\n"},
   698  		{".ssh/authorized_keys2", "auth2\nauth3\n"},
   699  
   700  		{".juju/my-name-cert.pem", caCert},
   701  		{".juju/my-name-private-key.pem", caKey},
   702  		{".juju/cacert2.pem", caCert2},
   703  		{".juju/cakey2.pem", caKey2},
   704  		{"othercert.pem", caCert3},
   705  		{"otherkey.pem", caKey3},
   706  	}
   707  	h := testing.MakeFakeHomeWithFiles(c, files)
   708  	defer h.Restore()
   709  	for i, test := range configTests {
   710  		c.Logf("test %d. %s", i, test.about)
   711  		test.check(c, h)
   712  	}
   713  }
   714  
   715  var noCertFilesTests = []configTest{
   716  	{
   717  		about:       "Unspecified certificate and key",
   718  		useDefaults: config.UseDefaults,
   719  		attrs: testing.Attrs{
   720  			"type":            "my-type",
   721  			"name":            "my-name",
   722  			"authorized-keys": testing.FakeAuthKeys,
   723  		},
   724  	}, {
   725  		about:       "Unspecified certificate, specified key",
   726  		useDefaults: config.UseDefaults,
   727  		attrs: testing.Attrs{
   728  			"type":            "my-type",
   729  			"name":            "my-name",
   730  			"authorized-keys": testing.FakeAuthKeys,
   731  			"ca-private-key":  caKey,
   732  		},
   733  		err: "bad CA certificate/key in configuration: crypto/tls:.*",
   734  	},
   735  }
   736  
   737  func (*ConfigSuite) TestConfigNoCertFiles(c *gc.C) {
   738  	h := testing.MakeEmptyFakeHome(c)
   739  	defer h.Restore()
   740  	for i, test := range noCertFilesTests {
   741  		c.Logf("test %d. %s", i, test.about)
   742  		test.check(c, h)
   743  	}
   744  }
   745  
   746  var emptyCertFilesTests = []configTest{
   747  	{
   748  		about:       "Cert unspecified; key specified",
   749  		useDefaults: config.UseDefaults,
   750  		attrs: testing.Attrs{
   751  			"type":            "my-type",
   752  			"name":            "my-name",
   753  			"authorized-keys": testing.FakeAuthKeys,
   754  			"ca-private-key":  caKey,
   755  		},
   756  		err: `file ".*/my-name-cert.pem" is empty`,
   757  	}, {
   758  		about:       "Cert and key unspecified",
   759  		useDefaults: config.UseDefaults,
   760  		attrs: testing.Attrs{
   761  			"type":            "my-type",
   762  			"name":            "my-name",
   763  			"authorized-keys": testing.FakeAuthKeys,
   764  		},
   765  		err: `file ".*/my-name-cert.pem" is empty`,
   766  	}, {
   767  		about:       "Cert specified, key unspecified",
   768  		useDefaults: config.UseDefaults,
   769  		attrs: testing.Attrs{
   770  			"type":            "my-type",
   771  			"name":            "my-name",
   772  			"authorized-keys": testing.FakeAuthKeys,
   773  			"ca-cert":         caCert,
   774  		},
   775  		err: `file ".*/my-name-private-key.pem" is empty`,
   776  	}, /* {
   777  		about: "Cert and key specified as absent",
   778  		useDefaults: config.UseDefaults,
   779  		attrs: testing.Attrs{
   780  			"type":            "my-type",
   781  			"name":            "my-name",
   782  			"authorized-keys": testing.FakeAuthKeys,
   783  			"ca-cert":         "",
   784  			"ca-private-key":  "",
   785  		},
   786  	}, {
   787  		about: "Cert specified as absent",
   788  		useDefaults: config.UseDefaults,
   789  		attrs: testing.Attrs{
   790  			"type":            "my-type",
   791  			"name":            "my-name",
   792  			"authorized-keys": testing.FakeAuthKeys,
   793  			"ca-cert":         "",
   794  		},
   795  		err: "bad CA certificate/key in configuration: crypto/tls: .*",
   796  	}, */
   797  }
   798  
   799  func (*ConfigSuite) TestConfigEmptyCertFiles(c *gc.C) {
   800  	files := []testing.TestFile{
   801  		{".juju/my-name-cert.pem", ""},
   802  		{".juju/my-name-private-key.pem", ""},
   803  	}
   804  	h := testing.MakeFakeHomeWithFiles(c, files)
   805  	defer h.Restore()
   806  
   807  	for i, test := range emptyCertFilesTests {
   808  		c.Logf("test %d. %s", i, test.about)
   809  		test.check(c, h)
   810  	}
   811  }
   812  
   813  func (test configTest) check(c *gc.C, home *testing.FakeHome) {
   814  	cfg, err := config.New(test.useDefaults, test.attrs)
   815  	if test.err != "" {
   816  		c.Check(cfg, gc.IsNil)
   817  		c.Assert(err, gc.ErrorMatches, test.err)
   818  		return
   819  	}
   820  	c.Assert(err, gc.IsNil)
   821  
   822  	typ, _ := test.attrs["type"].(string)
   823  	// "null" has been deprecated in favour of "manual",
   824  	// and is automatically switched.
   825  	if typ == "null" {
   826  		typ = "manual"
   827  	}
   828  	name, _ := test.attrs["name"].(string)
   829  	c.Assert(cfg.Type(), gc.Equals, typ)
   830  	c.Assert(cfg.Name(), gc.Equals, name)
   831  	agentVersion, ok := cfg.AgentVersion()
   832  	if s := test.attrs["agent-version"]; s != nil {
   833  		c.Assert(ok, jc.IsTrue)
   834  		c.Assert(agentVersion, gc.Equals, version.MustParse(s.(string)))
   835  	} else {
   836  		c.Assert(ok, jc.IsFalse)
   837  		c.Assert(agentVersion, gc.Equals, version.Zero)
   838  	}
   839  
   840  	if statePort, ok := test.attrs["state-port"]; ok {
   841  		c.Assert(cfg.StatePort(), gc.Equals, statePort)
   842  	}
   843  	if apiPort, ok := test.attrs["api-port"]; ok {
   844  		c.Assert(cfg.APIPort(), gc.Equals, apiPort)
   845  	}
   846  	if syslogPort, ok := test.attrs["syslog-port"]; ok {
   847  		c.Assert(cfg.SyslogPort(), gc.Equals, syslogPort)
   848  	}
   849  
   850  	dev, _ := test.attrs["development"].(bool)
   851  	c.Assert(cfg.Development(), gc.Equals, dev)
   852  
   853  	if series, _ := test.attrs["default-series"].(string); series != "" {
   854  		c.Assert(cfg.DefaultSeries(), gc.Equals, series)
   855  	} else {
   856  		c.Assert(cfg.DefaultSeries(), gc.Equals, config.DefaultSeries)
   857  	}
   858  	if m, _ := test.attrs["firewall-mode"].(string); m != "" {
   859  		c.Assert(cfg.FirewallMode(), gc.Equals, m)
   860  	}
   861  	if secret, _ := test.attrs["admin-secret"].(string); secret != "" {
   862  		c.Assert(cfg.AdminSecret(), gc.Equals, secret)
   863  	}
   864  
   865  	if path, _ := test.attrs["authorized-keys-path"].(string); path != "" {
   866  		c.Assert(cfg.AuthorizedKeys(), gc.Equals, home.FileContents(c, path))
   867  		c.Assert(cfg.AllAttrs()["authorized-keys-path"], gc.IsNil)
   868  	} else if keys, _ := test.attrs["authorized-keys"].(string); keys != "" {
   869  		c.Assert(cfg.AuthorizedKeys(), gc.Equals, keys)
   870  	} else {
   871  		// Content of all the files that are read by default.
   872  		c.Assert(cfg.AuthorizedKeys(), gc.Equals, "dsa\nrsa\nidentity\n")
   873  	}
   874  
   875  	cert, certPresent := cfg.CACert()
   876  	if path, _ := test.attrs["ca-cert-path"].(string); path != "" {
   877  		c.Assert(certPresent, jc.IsTrue)
   878  		c.Assert(string(cert), gc.Equals, home.FileContents(c, path))
   879  	} else if v, ok := test.attrs["ca-cert"].(string); v != "" {
   880  		c.Assert(certPresent, jc.IsTrue)
   881  		c.Assert(string(cert), gc.Equals, v)
   882  	} else if ok {
   883  		c.Check(cert, gc.HasLen, 0)
   884  		c.Assert(certPresent, jc.IsFalse)
   885  	} else if bool(test.useDefaults) && home.FileExists(".juju/my-name-cert.pem") {
   886  		c.Assert(certPresent, jc.IsTrue)
   887  		c.Assert(string(cert), gc.Equals, home.FileContents(c, "my-name-cert.pem"))
   888  	} else {
   889  		c.Check(cert, gc.HasLen, 0)
   890  		c.Assert(certPresent, jc.IsFalse)
   891  	}
   892  
   893  	key, keyPresent := cfg.CAPrivateKey()
   894  	if path, _ := test.attrs["ca-private-key-path"].(string); path != "" {
   895  		c.Assert(keyPresent, jc.IsTrue)
   896  		c.Assert(string(key), gc.Equals, home.FileContents(c, path))
   897  	} else if v, ok := test.attrs["ca-private-key"].(string); v != "" {
   898  		c.Assert(keyPresent, jc.IsTrue)
   899  		c.Assert(string(key), gc.Equals, v)
   900  	} else if ok {
   901  		c.Check(key, gc.HasLen, 0)
   902  		c.Assert(keyPresent, jc.IsFalse)
   903  	} else if bool(test.useDefaults) && home.FileExists(".juju/my-name-private-key.pem") {
   904  		c.Assert(keyPresent, jc.IsTrue)
   905  		c.Assert(string(key), gc.Equals, home.FileContents(c, "my-name-private-key.pem"))
   906  	} else {
   907  		c.Check(key, gc.HasLen, 0)
   908  		c.Assert(keyPresent, jc.IsFalse)
   909  	}
   910  
   911  	if v, ok := test.attrs["ssl-hostname-verification"]; ok {
   912  		c.Assert(cfg.SSLHostnameVerification(), gc.Equals, v)
   913  	}
   914  
   915  	if v, ok := test.attrs["provisioner-safe-mode"]; ok {
   916  		c.Assert(cfg.ProvisionerSafeMode(), gc.Equals, v)
   917  	} else {
   918  		c.Assert(cfg.ProvisionerSafeMode(), gc.Equals, false)
   919  	}
   920  	sshOpts := cfg.BootstrapSSHOpts()
   921  	test.assertDuration(
   922  		c,
   923  		"bootstrap-timeout",
   924  		sshOpts.Timeout,
   925  		config.DefaultBootstrapSSHTimeout,
   926  	)
   927  	test.assertDuration(
   928  		c,
   929  		"bootstrap-retry-delay",
   930  		sshOpts.RetryDelay,
   931  		config.DefaultBootstrapSSHRetryDelay,
   932  	)
   933  	test.assertDuration(
   934  		c,
   935  		"bootstrap-addresses-delay",
   936  		sshOpts.AddressesDelay,
   937  		config.DefaultBootstrapSSHAddressesDelay,
   938  	)
   939  
   940  	if v, ok := test.attrs["image-stream"]; ok {
   941  		c.Assert(cfg.ImageStream(), gc.Equals, v)
   942  	} else {
   943  		c.Assert(cfg.ImageStream(), gc.Equals, "released")
   944  	}
   945  
   946  	url, urlPresent := cfg.ImageMetadataURL()
   947  	if v, _ := test.attrs["image-metadata-url"].(string); v != "" {
   948  		c.Assert(url, gc.Equals, v)
   949  		c.Assert(urlPresent, jc.IsTrue)
   950  	} else {
   951  		c.Assert(urlPresent, jc.IsFalse)
   952  	}
   953  	toolsURL, urlPresent := cfg.ToolsURL()
   954  	oldToolsURL, oldURLPresent := cfg.AllAttrs()["tools-url"]
   955  	oldToolsURLAttrValue, oldURLAttrPresent := test.attrs["tools-url"]
   956  	expectedToolsURLValue := test.attrs["tools-metadata-url"]
   957  	if expectedToolsURLValue == nil {
   958  		expectedToolsURLValue = oldToolsURLAttrValue
   959  	}
   960  	if expectedToolsURLValue != nil && expectedToolsURLValue != "" {
   961  		c.Assert(expectedToolsURLValue, gc.Equals, "tools-metadata-url-value")
   962  		c.Assert(toolsURL, gc.Equals, expectedToolsURLValue)
   963  		c.Assert(urlPresent, jc.IsTrue)
   964  		c.Assert(oldToolsURL, gc.Equals, expectedToolsURLValue)
   965  		c.Assert(oldURLPresent, jc.IsTrue)
   966  	} else {
   967  		c.Assert(urlPresent, jc.IsFalse)
   968  		c.Assert(oldURLAttrPresent, jc.IsFalse)
   969  		c.Assert(oldToolsURL, gc.Equals, "")
   970  	}
   971  }
   972  
   973  func (test configTest) assertDuration(c *gc.C, name string, actual time.Duration, defaultInSeconds int) {
   974  	value, ok := test.attrs[name].(int)
   975  	if !ok || value == 0 {
   976  		c.Assert(actual, gc.Equals, time.Duration(defaultInSeconds)*time.Second)
   977  	} else {
   978  		c.Assert(actual, gc.Equals, time.Duration(value)*time.Second)
   979  	}
   980  }
   981  
   982  func (s *ConfigSuite) TestConfigAttrs(c *gc.C) {
   983  	// Normally this is handled by testing.FakeHome
   984  	s.PatchEnvironment(osenv.JujuLoggingConfigEnvKey, "")
   985  	attrs := map[string]interface{}{
   986  		"type":                      "my-type",
   987  		"name":                      "my-name",
   988  		"authorized-keys":           testing.FakeAuthKeys,
   989  		"firewall-mode":             config.FwInstance,
   990  		"admin-secret":              "foo",
   991  		"unknown":                   "my-unknown",
   992  		"ca-cert":                   caCert,
   993  		"ssl-hostname-verification": true,
   994  		"development":               false,
   995  		"provisioner-safe-mode":     false,
   996  		"state-port":                1234,
   997  		"api-port":                  4321,
   998  		"syslog-port":               2345,
   999  		"bootstrap-timeout":         3600,
  1000  		"bootstrap-retry-delay":     30,
  1001  		"bootstrap-addresses-delay": 10,
  1002  		"default-series":            "precise",
  1003  		"charm-store-auth":          "token=auth",
  1004  	}
  1005  	cfg, err := config.New(config.NoDefaults, attrs)
  1006  	c.Assert(err, gc.IsNil)
  1007  
  1008  	// These attributes are added if not set.
  1009  	attrs["development"] = false
  1010  	attrs["default-series"] = config.DefaultSeries
  1011  	attrs["logging-config"] = "<root>=WARNING;unit=DEBUG"
  1012  	attrs["ca-private-key"] = ""
  1013  	attrs["image-metadata-url"] = ""
  1014  	attrs["tools-metadata-url"] = ""
  1015  	attrs["tools-url"] = ""
  1016  	attrs["image-stream"] = ""
  1017  	// Default firewall mode is instance
  1018  	attrs["firewall-mode"] = string(config.FwInstance)
  1019  	c.Assert(cfg.AllAttrs(), jc.DeepEquals, attrs)
  1020  	c.Assert(cfg.UnknownAttrs(), jc.DeepEquals, map[string]interface{}{"unknown": "my-unknown"})
  1021  
  1022  	newcfg, err := cfg.Apply(map[string]interface{}{
  1023  		"name":        "new-name",
  1024  		"new-unknown": "my-new-unknown",
  1025  	})
  1026  	c.Assert(err, gc.IsNil)
  1027  
  1028  	attrs["name"] = "new-name"
  1029  	attrs["new-unknown"] = "my-new-unknown"
  1030  	c.Assert(newcfg.AllAttrs(), jc.DeepEquals, attrs)
  1031  }
  1032  
  1033  type validationTest struct {
  1034  	about string
  1035  	new   testing.Attrs
  1036  	old   testing.Attrs
  1037  	err   string
  1038  }
  1039  
  1040  var validationTests = []validationTest{{
  1041  	about: "Can't change the type",
  1042  	new:   testing.Attrs{"type": "new-type"},
  1043  	err:   `cannot change type from "my-type" to "new-type"`,
  1044  }, {
  1045  	about: "Can't change the name",
  1046  	new:   testing.Attrs{"name": "new-name"},
  1047  	err:   `cannot change name from "my-name" to "new-name"`,
  1048  }, {
  1049  	about: "Can set agent version",
  1050  	new:   testing.Attrs{"agent-version": "1.9.13"},
  1051  }, {
  1052  	about: "Can change agent version",
  1053  	old:   testing.Attrs{"agent-version": "1.9.13"},
  1054  	new:   testing.Attrs{"agent-version": "1.9.27"},
  1055  }, {
  1056  	about: "Can't clear agent version",
  1057  	old:   testing.Attrs{"agent-version": "1.9.27"},
  1058  	err:   `cannot clear agent-version`,
  1059  }, {
  1060  	about: "Can't change the firewall-mode",
  1061  	old:   testing.Attrs{"firewall-mode": config.FwGlobal},
  1062  	new:   testing.Attrs{"firewall-mode": config.FwInstance},
  1063  	err:   `cannot change firewall-mode from "global" to "instance"`,
  1064  }, {
  1065  	about: "Cannot change the state-port",
  1066  	old:   testing.Attrs{"state-port": config.DefaultStatePort},
  1067  	new:   testing.Attrs{"state-port": 42},
  1068  	err:   `cannot change state-port from 37017 to 42`,
  1069  }, {
  1070  	about: "Cannot change the api-port",
  1071  	old:   testing.Attrs{"api-port": config.DefaultAPIPort},
  1072  	new:   testing.Attrs{"api-port": 42},
  1073  	err:   `cannot change api-port from 17070 to 42`,
  1074  }, {
  1075  	about: "Cannot change the syslog-port",
  1076  	old:   testing.Attrs{"syslog-port": 345},
  1077  	new:   testing.Attrs{"syslog-port": 42},
  1078  	err:   `cannot change syslog-port from 345 to 42`,
  1079  }, {
  1080  	about: "Can change the state-port from explicit-default to implicit-default",
  1081  	old:   testing.Attrs{"state-port": config.DefaultStatePort},
  1082  }, {
  1083  	about: "Can change the api-port from explicit-default to implicit-default",
  1084  	old:   testing.Attrs{"api-port": config.DefaultAPIPort},
  1085  }, {
  1086  	about: "Can change the state-port from implicit-default to explicit-default",
  1087  	new:   testing.Attrs{"state-port": config.DefaultStatePort},
  1088  }, {
  1089  	about: "Can change the api-port from implicit-default to explicit-default",
  1090  	new:   testing.Attrs{"api-port": config.DefaultAPIPort},
  1091  }, {
  1092  	about: "Cannot change the state-port from implicit-default to different value",
  1093  	new:   testing.Attrs{"state-port": 42},
  1094  	err:   `cannot change state-port from 37017 to 42`,
  1095  }, {
  1096  	about: "Cannot change the api-port from implicit-default to different value",
  1097  	new:   testing.Attrs{"api-port": 42},
  1098  	err:   `cannot change api-port from 17070 to 42`,
  1099  }, {
  1100  	about: "Cannot change the syslog-port from implicit-default to different value",
  1101  	new:   testing.Attrs{"syslog-port": 42},
  1102  	err:   `cannot change syslog-port from 514 to 42`,
  1103  }}
  1104  
  1105  func (*ConfigSuite) TestValidateChange(c *gc.C) {
  1106  	files := []testing.TestFile{
  1107  		{".ssh/identity.pub", "identity"},
  1108  	}
  1109  	h := testing.MakeFakeHomeWithFiles(c, files)
  1110  	defer h.Restore()
  1111  
  1112  	for i, test := range validationTests {
  1113  		c.Logf("test %d: %s", i, test.about)
  1114  		newConfig := newTestConfig(c, test.new)
  1115  		oldConfig := newTestConfig(c, test.old)
  1116  		err := config.Validate(newConfig, oldConfig)
  1117  		if test.err == "" {
  1118  			c.Assert(err, gc.IsNil)
  1119  		} else {
  1120  			c.Assert(err, gc.ErrorMatches, test.err)
  1121  		}
  1122  	}
  1123  }
  1124  
  1125  func makeFakeHome(c *gc.C) *testing.FakeHome {
  1126  	return testing.MakeFakeHomeWithFiles(c, []testing.TestFile{
  1127  		{".ssh/id_rsa.pub", "rsa\n"},
  1128  		{".juju/myenv-cert.pem", caCert},
  1129  		{".juju/myenv-private-key.pem", caKey},
  1130  	})
  1131  }
  1132  
  1133  func (*ConfigSuite) TestValidateUnknownAttrs(c *gc.C) {
  1134  	defer makeFakeHome(c).Restore()
  1135  	cfg, err := config.New(config.UseDefaults, map[string]interface{}{
  1136  		"name":    "myenv",
  1137  		"type":    "other",
  1138  		"known":   "this",
  1139  		"unknown": "that",
  1140  	})
  1141  
  1142  	// No fields: all attrs passed through.
  1143  	attrs, err := cfg.ValidateUnknownAttrs(nil, nil)
  1144  	c.Assert(err, gc.IsNil)
  1145  	c.Assert(attrs, gc.DeepEquals, map[string]interface{}{
  1146  		"known":   "this",
  1147  		"unknown": "that",
  1148  	})
  1149  
  1150  	// Valid field: that and other attrs passed through.
  1151  	fields := schema.Fields{"known": schema.String()}
  1152  	attrs, err = cfg.ValidateUnknownAttrs(fields, nil)
  1153  	c.Assert(err, gc.IsNil)
  1154  	c.Assert(attrs, gc.DeepEquals, map[string]interface{}{
  1155  		"known":   "this",
  1156  		"unknown": "that",
  1157  	})
  1158  
  1159  	// Default field: inserted.
  1160  	fields["default"] = schema.String()
  1161  	defaults := schema.Defaults{"default": "the other"}
  1162  	attrs, err = cfg.ValidateUnknownAttrs(fields, defaults)
  1163  	c.Assert(err, gc.IsNil)
  1164  	c.Assert(attrs, gc.DeepEquals, map[string]interface{}{
  1165  		"known":   "this",
  1166  		"unknown": "that",
  1167  		"default": "the other",
  1168  	})
  1169  
  1170  	// Invalid field: failure.
  1171  	fields["known"] = schema.Int()
  1172  	_, err = cfg.ValidateUnknownAttrs(fields, defaults)
  1173  	c.Assert(err, gc.ErrorMatches, `known: expected int, got string\("this"\)`)
  1174  }
  1175  
  1176  func newTestConfig(c *gc.C, explicit testing.Attrs) *config.Config {
  1177  	final := testing.Attrs{"type": "my-type", "name": "my-name"}
  1178  	for key, value := range explicit {
  1179  		final[key] = value
  1180  	}
  1181  	result, err := config.New(config.UseDefaults, final)
  1182  	c.Assert(err, gc.IsNil)
  1183  	return result
  1184  }
  1185  
  1186  func (*ConfigSuite) TestLoggingConfig(c *gc.C) {
  1187  	defer makeFakeHome(c).Restore()
  1188  
  1189  	config := newTestConfig(c, testing.Attrs{
  1190  		"logging-config": "<root>=WARNING;juju=DEBUG"})
  1191  	c.Assert(config.LoggingConfig(), gc.Equals, "<root>=WARNING;juju=DEBUG;unit=DEBUG")
  1192  }
  1193  
  1194  func (*ConfigSuite) TestLoggingConfigWithUnit(c *gc.C) {
  1195  	defer makeFakeHome(c).Restore()
  1196  
  1197  	config := newTestConfig(c, testing.Attrs{
  1198  		"logging-config": "<root>=WARNING;unit=INFO"})
  1199  	c.Assert(config.LoggingConfig(), gc.Equals, "<root>=WARNING;unit=INFO")
  1200  }
  1201  
  1202  func (s *ConfigSuite) TestLoggingConfigFromEnvironment(c *gc.C) {
  1203  	defer makeFakeHome(c).Restore()
  1204  	s.PatchEnvironment(osenv.JujuLoggingConfigEnvKey, "<root>=INFO")
  1205  
  1206  	config := newTestConfig(c, nil)
  1207  	c.Assert(config.LoggingConfig(), gc.Equals, "<root>=INFO;unit=DEBUG")
  1208  }
  1209  
  1210  func (*ConfigSuite) TestProxyValuesWithFallback(c *gc.C) {
  1211  	defer makeFakeHome(c).Restore()
  1212  
  1213  	config := newTestConfig(c, testing.Attrs{
  1214  		"http-proxy":  "http://user@10.0.0.1",
  1215  		"https-proxy": "https://user@10.0.0.1",
  1216  		"ftp-proxy":   "ftp://user@10.0.0.1",
  1217  	})
  1218  	c.Assert(config.HttpProxy(), gc.Equals, "http://user@10.0.0.1")
  1219  	c.Assert(config.AptHttpProxy(), gc.Equals, "http://user@10.0.0.1")
  1220  	c.Assert(config.HttpsProxy(), gc.Equals, "https://user@10.0.0.1")
  1221  	c.Assert(config.AptHttpsProxy(), gc.Equals, "https://user@10.0.0.1")
  1222  	c.Assert(config.FtpProxy(), gc.Equals, "ftp://user@10.0.0.1")
  1223  	c.Assert(config.AptFtpProxy(), gc.Equals, "ftp://user@10.0.0.1")
  1224  }
  1225  
  1226  func (*ConfigSuite) TestProxyValues(c *gc.C) {
  1227  	defer makeFakeHome(c).Restore()
  1228  
  1229  	config := newTestConfig(c, testing.Attrs{
  1230  		"http-proxy":      "http://user@10.0.0.1",
  1231  		"https-proxy":     "https://user@10.0.0.1",
  1232  		"ftp-proxy":       "ftp://user@10.0.0.1",
  1233  		"apt-http-proxy":  "http://user@10.0.0.2",
  1234  		"apt-https-proxy": "https://user@10.0.0.2",
  1235  		"apt-ftp-proxy":   "ftp://user@10.0.0.2",
  1236  	})
  1237  	c.Assert(config.HttpProxy(), gc.Equals, "http://user@10.0.0.1")
  1238  	c.Assert(config.AptHttpProxy(), gc.Equals, "http://user@10.0.0.2")
  1239  	c.Assert(config.HttpsProxy(), gc.Equals, "https://user@10.0.0.1")
  1240  	c.Assert(config.AptHttpsProxy(), gc.Equals, "https://user@10.0.0.2")
  1241  	c.Assert(config.FtpProxy(), gc.Equals, "ftp://user@10.0.0.1")
  1242  	c.Assert(config.AptFtpProxy(), gc.Equals, "ftp://user@10.0.0.2")
  1243  }
  1244  
  1245  func (*ConfigSuite) TestProxyValuesNotSet(c *gc.C) {
  1246  	defer makeFakeHome(c).Restore()
  1247  
  1248  	config := newTestConfig(c, testing.Attrs{})
  1249  	c.Assert(config.HttpProxy(), gc.Equals, "")
  1250  	c.Assert(config.AptHttpProxy(), gc.Equals, "")
  1251  	c.Assert(config.HttpsProxy(), gc.Equals, "")
  1252  	c.Assert(config.AptHttpsProxy(), gc.Equals, "")
  1253  	c.Assert(config.FtpProxy(), gc.Equals, "")
  1254  	c.Assert(config.AptFtpProxy(), gc.Equals, "")
  1255  }
  1256  
  1257  func (*ConfigSuite) TestProxyConfigMap(c *gc.C) {
  1258  	defer makeFakeHome(c).Restore()
  1259  
  1260  	cfg := newTestConfig(c, testing.Attrs{})
  1261  	proxy := osenv.ProxySettings{
  1262  		Http:  "http proxy",
  1263  		Https: "https proxy",
  1264  		Ftp:   "ftp proxy",
  1265  	}
  1266  	cfg, err := cfg.Apply(config.ProxyConfigMap(proxy))
  1267  	c.Assert(err, gc.IsNil)
  1268  	c.Assert(cfg.ProxySettings(), gc.DeepEquals, proxy)
  1269  	c.Assert(cfg.AptProxySettings(), gc.DeepEquals, proxy)
  1270  }
  1271  
  1272  func (*ConfigSuite) TestAptProxyConfigMap(c *gc.C) {
  1273  	defer makeFakeHome(c).Restore()
  1274  
  1275  	cfg := newTestConfig(c, testing.Attrs{})
  1276  	proxy := osenv.ProxySettings{
  1277  		Http:  "http proxy",
  1278  		Https: "https proxy",
  1279  		Ftp:   "ftp proxy",
  1280  	}
  1281  	cfg, err := cfg.Apply(config.AptProxyConfigMap(proxy))
  1282  	c.Assert(err, gc.IsNil)
  1283  	// The default proxy settings should still be empty.
  1284  	c.Assert(cfg.ProxySettings(), gc.DeepEquals, osenv.ProxySettings{})
  1285  	c.Assert(cfg.AptProxySettings(), gc.DeepEquals, proxy)
  1286  }
  1287  
  1288  func (*ConfigSuite) TestGenerateStateServerCertAndKey(c *gc.C) {
  1289  	// In order to test missing certs, it checks the JUJU_HOME dir, so we need
  1290  	// a fake home.
  1291  	defer testing.MakeFakeHomeWithFiles(c, []testing.TestFile{
  1292  		{".ssh/id_rsa.pub", "rsa\n"},
  1293  	}).Restore()
  1294  
  1295  	for _, test := range []struct {
  1296  		configValues map[string]interface{}
  1297  		errMatch     string
  1298  	}{{
  1299  		configValues: map[string]interface{}{
  1300  			"name": "test-no-certs",
  1301  			"type": "dummy",
  1302  		},
  1303  		errMatch: "environment configuration has no ca-cert",
  1304  	}, {
  1305  		configValues: map[string]interface{}{
  1306  			"name":    "test-no-certs",
  1307  			"type":    "dummy",
  1308  			"ca-cert": testing.CACert,
  1309  		},
  1310  		errMatch: "environment configuration has no ca-private-key",
  1311  	}, {
  1312  		configValues: map[string]interface{}{
  1313  			"name":           "test-no-certs",
  1314  			"type":           "dummy",
  1315  			"ca-cert":        testing.CACert,
  1316  			"ca-private-key": testing.CAKey,
  1317  		},
  1318  	}} {
  1319  		cfg, err := config.New(config.UseDefaults, test.configValues)
  1320  		c.Assert(err, gc.IsNil)
  1321  		certPEM, keyPEM, err := cfg.GenerateStateServerCertAndKey()
  1322  		if test.errMatch == "" {
  1323  			c.Assert(err, gc.IsNil)
  1324  
  1325  			_, _, err = cert.ParseCertAndKey(certPEM, keyPEM)
  1326  			c.Check(err, gc.IsNil)
  1327  
  1328  			err = cert.Verify(certPEM, []byte(testing.CACert), time.Now())
  1329  			c.Assert(err, gc.IsNil)
  1330  			err = cert.Verify(certPEM, []byte(testing.CACert), time.Now().AddDate(9, 0, 0))
  1331  			c.Assert(err, gc.IsNil)
  1332  			err = cert.Verify(certPEM, []byte(testing.CACert), time.Now().AddDate(10, 0, 1))
  1333  			c.Assert(err, gc.NotNil)
  1334  		} else {
  1335  			c.Assert(err, gc.ErrorMatches, test.errMatch)
  1336  			c.Assert(certPEM, gc.IsNil)
  1337  			c.Assert(keyPEM, gc.IsNil)
  1338  		}
  1339  	}
  1340  }
  1341  
  1342  var caCert = `
  1343  -----BEGIN CERTIFICATE-----
  1344  MIIBjDCCATigAwIBAgIBADALBgkqhkiG9w0BAQUwHjENMAsGA1UEChMEanVqdTEN
  1345  MAsGA1UEAxMEcm9vdDAeFw0xMjExMDkxNjQwMjhaFw0yMjExMDkxNjQ1MjhaMB4x
  1346  DTALBgNVBAoTBGp1anUxDTALBgNVBAMTBHJvb3QwWTALBgkqhkiG9w0BAQEDSgAw
  1347  RwJAduA1Gnb2VJLxNGfG4St0Qy48Y3q5Z5HheGtTGmti/FjlvQvScCFGCnJG7fKA
  1348  Knd7ia3vWg7lxYkIvMPVP88LAQIDAQABo2YwZDAOBgNVHQ8BAf8EBAMCAKQwEgYD
  1349  VR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUlvKX8vwp0o+VdhdhoA9O6KlOm00w
  1350  HwYDVR0jBBgwFoAUlvKX8vwp0o+VdhdhoA9O6KlOm00wCwYJKoZIhvcNAQEFA0EA
  1351  LlNpevtFr8gngjAFFAO/FXc7KiZcCrA5rBfb/rEy297lIqmKt5++aVbLEPyxCIFC
  1352  r71Sj63TUTFWtRZAxvn9qQ==
  1353  -----END CERTIFICATE-----
  1354  `[1:]
  1355  
  1356  var caKey = `
  1357  -----BEGIN RSA PRIVATE KEY-----
  1358  MIIBOQIBAAJAduA1Gnb2VJLxNGfG4St0Qy48Y3q5Z5HheGtTGmti/FjlvQvScCFG
  1359  CnJG7fKAKnd7ia3vWg7lxYkIvMPVP88LAQIDAQABAkEAsFOdMSYn+AcF1M/iBfjo
  1360  uQWJ+Zz+CgwuvumjGNsUtmwxjA+hh0fCn0Ah2nAt4Ma81vKOKOdQ8W6bapvsVDH0
  1361  6QIhAJOkLmEKm4H5POQV7qunRbRsLbft/n/SHlOBz165WFvPAiEAzh9fMf70std1
  1362  sVCHJRQWKK+vw3oaEvPKvkPiV5ui0C8CIGNsvybuo8ald5IKCw5huRlFeIxSo36k
  1363  m3OVCXc6zfwVAiBnTUe7WcivPNZqOC6TAZ8dYvdWo4Ifz3jjpEfymjid1wIgBIJv
  1364  ERPyv2NQqIFQZIyzUP7LVRIWfpFFOo9/Ww/7s5Y=
  1365  -----END RSA PRIVATE KEY-----
  1366  `[1:]
  1367  
  1368  var caCert2 = `
  1369  -----BEGIN CERTIFICATE-----
  1370  MIIBjTCCATmgAwIBAgIBADALBgkqhkiG9w0BAQUwHjENMAsGA1UEChMEanVqdTEN
  1371  MAsGA1UEAxMEcm9vdDAeFw0xMjExMDkxNjQxMDhaFw0yMjExMDkxNjQ2MDhaMB4x
  1372  DTALBgNVBAoTBGp1anUxDTALBgNVBAMTBHJvb3QwWjALBgkqhkiG9w0BAQEDSwAw
  1373  SAJBAJkSWRrr81y8pY4dbNgt+8miSKg4z6glp2KO2NnxxAhyyNtQHKvC+fJALJj+
  1374  C2NhuvOv9xImxOl3Hg8fFPCXCtcCAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgCkMBIG
  1375  A1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFOsX/ZCqKzWCAaTTVcWsWKT5Msow
  1376  MB8GA1UdIwQYMBaAFOsX/ZCqKzWCAaTTVcWsWKT5MsowMAsGCSqGSIb3DQEBBQNB
  1377  AAVV57jetEzJQnjgBzhvx/UwauFn78jGhXfV5BrQmxIb4SF4DgSCFstPwUQOAr8h
  1378  XXzJqBQH92KYmp+y3YXDoMQ=
  1379  -----END CERTIFICATE-----
  1380  `[1:]
  1381  
  1382  var caKey2 = `
  1383  -----BEGIN RSA PRIVATE KEY-----
  1384  MIIBOQIBAAJBAJkSWRrr81y8pY4dbNgt+8miSKg4z6glp2KO2NnxxAhyyNtQHKvC
  1385  +fJALJj+C2NhuvOv9xImxOl3Hg8fFPCXCtcCAwEAAQJATQNzO11NQvJS5U6eraFt
  1386  FgSFQ8XZjILtVWQDbJv8AjdbEgKMHEy33icsAKIUAx8jL9kjq6K9kTdAKXZi9grF
  1387  UQIhAPD7jccIDUVm785E5eR9eisq0+xpgUIa24Jkn8cAlst5AiEAopxVFl1auer3
  1388  GP2In3pjdL4ydzU/gcRcYisoJqwHpM8CIHtqmaXBPeq5WT9ukb5/dL3+5SJCtmxA
  1389  jQMuvZWRe6khAiBvMztYtPSDKXRbCZ4xeQ+kWSDHtok8Y5zNoTeu4nvDrwIgb3Al
  1390  fikzPveC5g6S6OvEQmyDz59tYBubm2XHgvxqww0=
  1391  -----END RSA PRIVATE KEY-----
  1392  `[1:]
  1393  
  1394  var caCert3 = `
  1395  -----BEGIN CERTIFICATE-----
  1396  MIIBjTCCATmgAwIBAgIBADALBgkqhkiG9w0BAQUwHjENMAsGA1UEChMEanVqdTEN
  1397  MAsGA1UEAxMEcm9vdDAeFw0xMjExMDkxNjQxMjlaFw0yMjExMDkxNjQ2MjlaMB4x
  1398  DTALBgNVBAoTBGp1anUxDTALBgNVBAMTBHJvb3QwWjALBgkqhkiG9w0BAQEDSwAw
  1399  SAJBAIW7CbHFJivvV9V6mO8AGzJS9lqjUf6MdEPsdF6wx2Cpzr/lSFIggCwRA138
  1400  9MuFxflxb/3U8Nq+rd8rVtTgFMECAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgCkMBIG
  1401  A1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFJafrxqByMN9BwGfcmuF0Lw/1QII
  1402  MB8GA1UdIwQYMBaAFJafrxqByMN9BwGfcmuF0Lw/1QIIMAsGCSqGSIb3DQEBBQNB
  1403  AHq3vqNhxya3s33DlQfSj9whsnqM0Nm+u8mBX/T76TF5rV7+B33XmYzSyfA3yBi/
  1404  zHaUR/dbHuiNTO+KXs3/+Y4=
  1405  -----END CERTIFICATE-----
  1406  `[1:]
  1407  
  1408  var caKey3 = `
  1409  -----BEGIN RSA PRIVATE KEY-----
  1410  MIIBOgIBAAJBAIW7CbHFJivvV9V6mO8AGzJS9lqjUf6MdEPsdF6wx2Cpzr/lSFIg
  1411  gCwRA1389MuFxflxb/3U8Nq+rd8rVtTgFMECAwEAAQJAaivPi4qJPrJb2onl50H/
  1412  VZnWKqmljGF4YQDWduMEt7GTPk+76x9SpO7W4gfY490Ivd9DEXfbr/KZqhwWikNw
  1413  LQIhALlLfRXLF2ZfToMfB1v1v+jith5onAu24O68mkdRc5PLAiEAuMJ/6U07hggr
  1414  Ckf9OT93wh84DK66h780HJ/FUHKcoCMCIDsPZaJBpoa50BOZG0ZjcTTwti3BGCPf
  1415  uZg+w0oCGz27AiEAsUCYKqEXy/ymHhT2kSecozYENdajyXvcaOG3EPkD3nUCICOP
  1416  zatzs7c/4mx4a0JBG6Za0oEPUcm2I34is50KSohz
  1417  -----END RSA PRIVATE KEY-----
  1418  `[1:]
  1419  
  1420  var invalidCAKey = `
  1421  -----BEGIN RSA PRIVATE KEY-----
  1422  MIIBOgIBAAJAZabKgKInuOxj5vDWLwHHQtK3/45KB+32D15w94Nt83BmuGxo90lw
  1423  -----END RSA PRIVATE KEY-----
  1424  `[1:]
  1425  
  1426  var invalidCACert = `
  1427  -----BEGIN CERTIFICATE-----
  1428  MIIBOgIBAAJAZabKgKInuOxj5vDWLwHHQtK3/45KB+32D15w94Nt83BmuGxo90lw
  1429  -----END CERTIFICATE-----
  1430  `[1:]