...

Source file src/github.com/docker/distribution/configuration/configuration_test.go

Documentation: github.com/docker/distribution/configuration

     1  package configuration
     2  
     3  import (
     4  	"bytes"
     5  	"net/http"
     6  	"os"
     7  	"reflect"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	. "gopkg.in/check.v1"
    13  	"gopkg.in/yaml.v2"
    14  )
    15  
    16  // Hook up gocheck into the "go test" runner
    17  func Test(t *testing.T) { TestingT(t) }
    18  
    19  // configStruct is a canonical example configuration, which should map to configYamlV0_1
    20  var configStruct = Configuration{
    21  	Version: "0.1",
    22  	Log: struct {
    23  		AccessLog struct {
    24  			Disabled bool `yaml:"disabled,omitempty"`
    25  		} `yaml:"accesslog,omitempty"`
    26  		Level     Loglevel               `yaml:"level,omitempty"`
    27  		Formatter string                 `yaml:"formatter,omitempty"`
    28  		Fields    map[string]interface{} `yaml:"fields,omitempty"`
    29  		Hooks     []LogHook              `yaml:"hooks,omitempty"`
    30  	}{
    31  		Level:  "info",
    32  		Fields: map[string]interface{}{"environment": "test"},
    33  	},
    34  	Storage: Storage{
    35  		"s3": Parameters{
    36  			"region":        "us-east-1",
    37  			"bucket":        "my-bucket",
    38  			"rootdirectory": "/registry",
    39  			"encrypt":       true,
    40  			"secure":        false,
    41  			"accesskey":     "SAMPLEACCESSKEY",
    42  			"secretkey":     "SUPERSECRET",
    43  			"host":          nil,
    44  			"port":          42,
    45  		},
    46  	},
    47  	Auth: Auth{
    48  		"silly": Parameters{
    49  			"realm":   "silly",
    50  			"service": "silly",
    51  		},
    52  	},
    53  	Reporting: Reporting{
    54  		Bugsnag: BugsnagReporting{
    55  			APIKey: "BugsnagApiKey",
    56  		},
    57  	},
    58  	Notifications: Notifications{
    59  		Endpoints: []Endpoint{
    60  			{
    61  				Name: "endpoint-1",
    62  				URL:  "http://example.com",
    63  				Headers: http.Header{
    64  					"Authorization": []string{"Bearer <example>"},
    65  				},
    66  				IgnoredMediaTypes: []string{"application/octet-stream"},
    67  				Ignore: Ignore{
    68  					MediaTypes: []string{"application/octet-stream"},
    69  					Actions:    []string{"pull"},
    70  				},
    71  			},
    72  		},
    73  	},
    74  	Catalog: Catalog{
    75  		MaxEntries: 1000,
    76  	},
    77  	HTTP: struct {
    78  		Addr         string        `yaml:"addr,omitempty"`
    79  		Net          string        `yaml:"net,omitempty"`
    80  		Host         string        `yaml:"host,omitempty"`
    81  		Prefix       string        `yaml:"prefix,omitempty"`
    82  		Secret       string        `yaml:"secret,omitempty"`
    83  		RelativeURLs bool          `yaml:"relativeurls,omitempty"`
    84  		DrainTimeout time.Duration `yaml:"draintimeout,omitempty"`
    85  		TLS          struct {
    86  			Certificate  string   `yaml:"certificate,omitempty"`
    87  			Key          string   `yaml:"key,omitempty"`
    88  			ClientCAs    []string `yaml:"clientcas,omitempty"`
    89  			MinimumTLS   string   `yaml:"minimumtls,omitempty"`
    90  			CipherSuites []string `yaml:"ciphersuites,omitempty"`
    91  			LetsEncrypt  struct {
    92  				CacheFile string   `yaml:"cachefile,omitempty"`
    93  				Email     string   `yaml:"email,omitempty"`
    94  				Hosts     []string `yaml:"hosts,omitempty"`
    95  			} `yaml:"letsencrypt,omitempty"`
    96  		} `yaml:"tls,omitempty"`
    97  		Headers http.Header `yaml:"headers,omitempty"`
    98  		Debug   struct {
    99  			Addr       string `yaml:"addr,omitempty"`
   100  			Prometheus struct {
   101  				Enabled bool   `yaml:"enabled,omitempty"`
   102  				Path    string `yaml:"path,omitempty"`
   103  			} `yaml:"prometheus,omitempty"`
   104  		} `yaml:"debug,omitempty"`
   105  		HTTP2 struct {
   106  			Disabled bool `yaml:"disabled,omitempty"`
   107  		} `yaml:"http2,omitempty"`
   108  	}{
   109  		TLS: struct {
   110  			Certificate  string   `yaml:"certificate,omitempty"`
   111  			Key          string   `yaml:"key,omitempty"`
   112  			ClientCAs    []string `yaml:"clientcas,omitempty"`
   113  			MinimumTLS   string   `yaml:"minimumtls,omitempty"`
   114  			CipherSuites []string `yaml:"ciphersuites,omitempty"`
   115  			LetsEncrypt  struct {
   116  				CacheFile string   `yaml:"cachefile,omitempty"`
   117  				Email     string   `yaml:"email,omitempty"`
   118  				Hosts     []string `yaml:"hosts,omitempty"`
   119  			} `yaml:"letsencrypt,omitempty"`
   120  		}{
   121  			ClientCAs: []string{"/path/to/ca.pem"},
   122  		},
   123  		Headers: http.Header{
   124  			"X-Content-Type-Options": []string{"nosniff"},
   125  		},
   126  		HTTP2: struct {
   127  			Disabled bool `yaml:"disabled,omitempty"`
   128  		}{
   129  			Disabled: false,
   130  		},
   131  	},
   132  }
   133  
   134  // configYamlV0_1 is a Version 0.1 yaml document representing configStruct
   135  var configYamlV0_1 = `
   136  version: 0.1
   137  log:
   138    level: info
   139    fields:
   140      environment: test
   141  storage:
   142    s3:
   143      region: us-east-1
   144      bucket: my-bucket
   145      rootdirectory: /registry
   146      encrypt: true
   147      secure: false
   148      accesskey: SAMPLEACCESSKEY
   149      secretkey: SUPERSECRET
   150      host: ~
   151      port: 42
   152  auth:
   153    silly:
   154      realm: silly
   155      service: silly
   156  notifications:
   157    endpoints:
   158      - name: endpoint-1
   159        url:  http://example.com
   160        headers:
   161          Authorization: [Bearer <example>]
   162        ignoredmediatypes:
   163          - application/octet-stream
   164        ignore:
   165          mediatypes:
   166             - application/octet-stream
   167          actions:
   168             - pull
   169  reporting:
   170    bugsnag:
   171      apikey: BugsnagApiKey
   172  http:
   173    clientcas:
   174      - /path/to/ca.pem
   175    headers:
   176      X-Content-Type-Options: [nosniff]
   177  `
   178  
   179  // inmemoryConfigYamlV0_1 is a Version 0.1 yaml document specifying an inmemory
   180  // storage driver with no parameters
   181  var inmemoryConfigYamlV0_1 = `
   182  version: 0.1
   183  log:
   184    level: info
   185  storage: inmemory
   186  auth:
   187    silly:
   188      realm: silly
   189      service: silly
   190  notifications:
   191    endpoints:
   192      - name: endpoint-1
   193        url:  http://example.com
   194        headers:
   195          Authorization: [Bearer <example>]
   196        ignoredmediatypes:
   197          - application/octet-stream
   198        ignore:
   199          mediatypes:
   200             - application/octet-stream
   201          actions:
   202             - pull
   203  http:
   204    headers:
   205      X-Content-Type-Options: [nosniff]
   206  `
   207  
   208  type ConfigSuite struct {
   209  	expectedConfig *Configuration
   210  }
   211  
   212  var _ = Suite(new(ConfigSuite))
   213  
   214  func (suite *ConfigSuite) SetUpTest(c *C) {
   215  	os.Clearenv()
   216  	suite.expectedConfig = copyConfig(configStruct)
   217  }
   218  
   219  // TestMarshalRoundtrip validates that configStruct can be marshaled and
   220  // unmarshaled without changing any parameters
   221  func (suite *ConfigSuite) TestMarshalRoundtrip(c *C) {
   222  	configBytes, err := yaml.Marshal(suite.expectedConfig)
   223  	c.Assert(err, IsNil)
   224  	config, err := Parse(bytes.NewReader(configBytes))
   225  	c.Log(string(configBytes))
   226  	c.Assert(err, IsNil)
   227  	c.Assert(config, DeepEquals, suite.expectedConfig)
   228  }
   229  
   230  // TestParseSimple validates that configYamlV0_1 can be parsed into a struct
   231  // matching configStruct
   232  func (suite *ConfigSuite) TestParseSimple(c *C) {
   233  	config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
   234  	c.Assert(err, IsNil)
   235  	c.Assert(config, DeepEquals, suite.expectedConfig)
   236  }
   237  
   238  // TestParseInmemory validates that configuration yaml with storage provided as
   239  // a string can be parsed into a Configuration struct with no storage parameters
   240  func (suite *ConfigSuite) TestParseInmemory(c *C) {
   241  	suite.expectedConfig.Storage = Storage{"inmemory": Parameters{}}
   242  	suite.expectedConfig.Reporting = Reporting{}
   243  	suite.expectedConfig.Log.Fields = nil
   244  
   245  	config, err := Parse(bytes.NewReader([]byte(inmemoryConfigYamlV0_1)))
   246  	c.Assert(err, IsNil)
   247  	c.Assert(config, DeepEquals, suite.expectedConfig)
   248  }
   249  
   250  // TestParseIncomplete validates that an incomplete yaml configuration cannot
   251  // be parsed without providing environment variables to fill in the missing
   252  // components.
   253  func (suite *ConfigSuite) TestParseIncomplete(c *C) {
   254  	incompleteConfigYaml := "version: 0.1"
   255  	_, err := Parse(bytes.NewReader([]byte(incompleteConfigYaml)))
   256  	c.Assert(err, NotNil)
   257  
   258  	suite.expectedConfig.Log.Fields = nil
   259  	suite.expectedConfig.Storage = Storage{"filesystem": Parameters{"rootdirectory": "/tmp/testroot"}}
   260  	suite.expectedConfig.Auth = Auth{"silly": Parameters{"realm": "silly"}}
   261  	suite.expectedConfig.Reporting = Reporting{}
   262  	suite.expectedConfig.Notifications = Notifications{}
   263  	suite.expectedConfig.HTTP.Headers = nil
   264  
   265  	// Note: this also tests that REGISTRY_STORAGE and
   266  	// REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY can be used together
   267  	os.Setenv("REGISTRY_STORAGE", "filesystem")
   268  	os.Setenv("REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY", "/tmp/testroot")
   269  	os.Setenv("REGISTRY_AUTH", "silly")
   270  	os.Setenv("REGISTRY_AUTH_SILLY_REALM", "silly")
   271  
   272  	config, err := Parse(bytes.NewReader([]byte(incompleteConfigYaml)))
   273  	c.Assert(err, IsNil)
   274  	c.Assert(config, DeepEquals, suite.expectedConfig)
   275  }
   276  
   277  // TestParseWithSameEnvStorage validates that providing environment variables
   278  // that match the given storage type will only include environment-defined
   279  // parameters and remove yaml-defined parameters
   280  func (suite *ConfigSuite) TestParseWithSameEnvStorage(c *C) {
   281  	suite.expectedConfig.Storage = Storage{"s3": Parameters{"region": "us-east-1"}}
   282  
   283  	os.Setenv("REGISTRY_STORAGE", "s3")
   284  	os.Setenv("REGISTRY_STORAGE_S3_REGION", "us-east-1")
   285  
   286  	config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
   287  	c.Assert(err, IsNil)
   288  	c.Assert(config, DeepEquals, suite.expectedConfig)
   289  }
   290  
   291  // TestParseWithDifferentEnvStorageParams validates that providing environment variables that change
   292  // and add to the given storage parameters will change and add parameters to the parsed
   293  // Configuration struct
   294  func (suite *ConfigSuite) TestParseWithDifferentEnvStorageParams(c *C) {
   295  	suite.expectedConfig.Storage.setParameter("region", "us-west-1")
   296  	suite.expectedConfig.Storage.setParameter("secure", true)
   297  	suite.expectedConfig.Storage.setParameter("newparam", "some Value")
   298  
   299  	os.Setenv("REGISTRY_STORAGE_S3_REGION", "us-west-1")
   300  	os.Setenv("REGISTRY_STORAGE_S3_SECURE", "true")
   301  	os.Setenv("REGISTRY_STORAGE_S3_NEWPARAM", "some Value")
   302  
   303  	config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
   304  	c.Assert(err, IsNil)
   305  	c.Assert(config, DeepEquals, suite.expectedConfig)
   306  }
   307  
   308  // TestParseWithDifferentEnvStorageType validates that providing an environment variable that
   309  // changes the storage type will be reflected in the parsed Configuration struct
   310  func (suite *ConfigSuite) TestParseWithDifferentEnvStorageType(c *C) {
   311  	suite.expectedConfig.Storage = Storage{"inmemory": Parameters{}}
   312  
   313  	os.Setenv("REGISTRY_STORAGE", "inmemory")
   314  
   315  	config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
   316  	c.Assert(err, IsNil)
   317  	c.Assert(config, DeepEquals, suite.expectedConfig)
   318  }
   319  
   320  // TestParseWithDifferentEnvStorageTypeAndParams validates that providing an environment variable
   321  // that changes the storage type will be reflected in the parsed Configuration struct and that
   322  // environment storage parameters will also be included
   323  func (suite *ConfigSuite) TestParseWithDifferentEnvStorageTypeAndParams(c *C) {
   324  	suite.expectedConfig.Storage = Storage{"filesystem": Parameters{}}
   325  	suite.expectedConfig.Storage.setParameter("rootdirectory", "/tmp/testroot")
   326  
   327  	os.Setenv("REGISTRY_STORAGE", "filesystem")
   328  	os.Setenv("REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY", "/tmp/testroot")
   329  
   330  	config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
   331  	c.Assert(err, IsNil)
   332  	c.Assert(config, DeepEquals, suite.expectedConfig)
   333  }
   334  
   335  // TestParseWithSameEnvLoglevel validates that providing an environment variable defining the log
   336  // level to the same as the one provided in the yaml will not change the parsed Configuration struct
   337  func (suite *ConfigSuite) TestParseWithSameEnvLoglevel(c *C) {
   338  	os.Setenv("REGISTRY_LOGLEVEL", "info")
   339  
   340  	config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
   341  	c.Assert(err, IsNil)
   342  	c.Assert(config, DeepEquals, suite.expectedConfig)
   343  }
   344  
   345  // TestParseWithDifferentEnvLoglevel validates that providing an environment variable defining the
   346  // log level will override the value provided in the yaml document
   347  func (suite *ConfigSuite) TestParseWithDifferentEnvLoglevel(c *C) {
   348  	suite.expectedConfig.Log.Level = "error"
   349  
   350  	os.Setenv("REGISTRY_LOG_LEVEL", "error")
   351  
   352  	config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
   353  	c.Assert(err, IsNil)
   354  	c.Assert(config, DeepEquals, suite.expectedConfig)
   355  }
   356  
   357  // TestParseInvalidLoglevel validates that the parser will fail to parse a
   358  // configuration if the loglevel is malformed
   359  func (suite *ConfigSuite) TestParseInvalidLoglevel(c *C) {
   360  	invalidConfigYaml := "version: 0.1\nloglevel: derp\nstorage: inmemory"
   361  	_, err := Parse(bytes.NewReader([]byte(invalidConfigYaml)))
   362  	c.Assert(err, NotNil)
   363  
   364  	os.Setenv("REGISTRY_LOGLEVEL", "derp")
   365  
   366  	_, err = Parse(bytes.NewReader([]byte(configYamlV0_1)))
   367  	c.Assert(err, NotNil)
   368  
   369  }
   370  
   371  // TestParseWithDifferentEnvReporting validates that environment variables
   372  // properly override reporting parameters
   373  func (suite *ConfigSuite) TestParseWithDifferentEnvReporting(c *C) {
   374  	suite.expectedConfig.Reporting.Bugsnag.APIKey = "anotherBugsnagApiKey"
   375  	suite.expectedConfig.Reporting.Bugsnag.Endpoint = "localhost:8080"
   376  	suite.expectedConfig.Reporting.NewRelic.LicenseKey = "NewRelicLicenseKey"
   377  	suite.expectedConfig.Reporting.NewRelic.Name = "some NewRelic NAME"
   378  
   379  	os.Setenv("REGISTRY_REPORTING_BUGSNAG_APIKEY", "anotherBugsnagApiKey")
   380  	os.Setenv("REGISTRY_REPORTING_BUGSNAG_ENDPOINT", "localhost:8080")
   381  	os.Setenv("REGISTRY_REPORTING_NEWRELIC_LICENSEKEY", "NewRelicLicenseKey")
   382  	os.Setenv("REGISTRY_REPORTING_NEWRELIC_NAME", "some NewRelic NAME")
   383  
   384  	config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
   385  	c.Assert(err, IsNil)
   386  	c.Assert(config, DeepEquals, suite.expectedConfig)
   387  }
   388  
   389  // TestParseInvalidVersion validates that the parser will fail to parse a newer configuration
   390  // version than the CurrentVersion
   391  func (suite *ConfigSuite) TestParseInvalidVersion(c *C) {
   392  	suite.expectedConfig.Version = MajorMinorVersion(CurrentVersion.Major(), CurrentVersion.Minor()+1)
   393  	configBytes, err := yaml.Marshal(suite.expectedConfig)
   394  	c.Assert(err, IsNil)
   395  	_, err = Parse(bytes.NewReader(configBytes))
   396  	c.Assert(err, NotNil)
   397  }
   398  
   399  // TestParseExtraneousVars validates that environment variables referring to
   400  // nonexistent variables don't cause side effects.
   401  func (suite *ConfigSuite) TestParseExtraneousVars(c *C) {
   402  	suite.expectedConfig.Reporting.Bugsnag.Endpoint = "localhost:8080"
   403  
   404  	// A valid environment variable
   405  	os.Setenv("REGISTRY_REPORTING_BUGSNAG_ENDPOINT", "localhost:8080")
   406  
   407  	// Environment variables which shouldn't set config items
   408  	os.Setenv("registry_REPORTING_NEWRELIC_LICENSEKEY", "NewRelicLicenseKey")
   409  	os.Setenv("REPORTING_NEWRELIC_NAME", "some NewRelic NAME")
   410  	os.Setenv("REGISTRY_DUCKS", "quack")
   411  	os.Setenv("REGISTRY_REPORTING_ASDF", "ghjk")
   412  
   413  	config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
   414  	c.Assert(err, IsNil)
   415  	c.Assert(config, DeepEquals, suite.expectedConfig)
   416  }
   417  
   418  // TestParseEnvVarImplicitMaps validates that environment variables can set
   419  // values in maps that don't already exist.
   420  func (suite *ConfigSuite) TestParseEnvVarImplicitMaps(c *C) {
   421  	readonly := make(map[string]interface{})
   422  	readonly["enabled"] = true
   423  
   424  	maintenance := make(map[string]interface{})
   425  	maintenance["readonly"] = readonly
   426  
   427  	suite.expectedConfig.Storage["maintenance"] = maintenance
   428  
   429  	os.Setenv("REGISTRY_STORAGE_MAINTENANCE_READONLY_ENABLED", "true")
   430  
   431  	config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
   432  	c.Assert(err, IsNil)
   433  	c.Assert(config, DeepEquals, suite.expectedConfig)
   434  }
   435  
   436  // TestParseEnvWrongTypeMap validates that incorrectly attempting to unmarshal a
   437  // string over existing map fails.
   438  func (suite *ConfigSuite) TestParseEnvWrongTypeMap(c *C) {
   439  	os.Setenv("REGISTRY_STORAGE_S3", "somestring")
   440  
   441  	_, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
   442  	c.Assert(err, NotNil)
   443  }
   444  
   445  // TestParseEnvWrongTypeStruct validates that incorrectly attempting to
   446  // unmarshal a string into a struct fails.
   447  func (suite *ConfigSuite) TestParseEnvWrongTypeStruct(c *C) {
   448  	os.Setenv("REGISTRY_STORAGE_LOG", "somestring")
   449  
   450  	_, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
   451  	c.Assert(err, NotNil)
   452  }
   453  
   454  // TestParseEnvWrongTypeSlice validates that incorrectly attempting to
   455  // unmarshal a string into a slice fails.
   456  func (suite *ConfigSuite) TestParseEnvWrongTypeSlice(c *C) {
   457  	os.Setenv("REGISTRY_LOG_HOOKS", "somestring")
   458  
   459  	_, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
   460  	c.Assert(err, NotNil)
   461  }
   462  
   463  // TestParseEnvMany tests several environment variable overrides.
   464  // The result is not checked - the goal of this test is to detect panics
   465  // from misuse of reflection.
   466  func (suite *ConfigSuite) TestParseEnvMany(c *C) {
   467  	os.Setenv("REGISTRY_VERSION", "0.1")
   468  	os.Setenv("REGISTRY_LOG_LEVEL", "debug")
   469  	os.Setenv("REGISTRY_LOG_FORMATTER", "json")
   470  	os.Setenv("REGISTRY_LOG_HOOKS", "json")
   471  	os.Setenv("REGISTRY_LOG_FIELDS", "abc: xyz")
   472  	os.Setenv("REGISTRY_LOG_HOOKS", "- type: asdf")
   473  	os.Setenv("REGISTRY_LOGLEVEL", "debug")
   474  	os.Setenv("REGISTRY_STORAGE", "s3")
   475  	os.Setenv("REGISTRY_AUTH_PARAMS", "param1: value1")
   476  	os.Setenv("REGISTRY_AUTH_PARAMS_VALUE2", "value2")
   477  	os.Setenv("REGISTRY_AUTH_PARAMS_VALUE2", "value2")
   478  
   479  	_, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
   480  	c.Assert(err, IsNil)
   481  }
   482  
   483  func checkStructs(c *C, t reflect.Type, structsChecked map[string]struct{}) {
   484  	for t.Kind() == reflect.Ptr || t.Kind() == reflect.Map || t.Kind() == reflect.Slice {
   485  		t = t.Elem()
   486  	}
   487  
   488  	if t.Kind() != reflect.Struct {
   489  		return
   490  	}
   491  	if _, present := structsChecked[t.String()]; present {
   492  		// Already checked this type
   493  		return
   494  	}
   495  
   496  	structsChecked[t.String()] = struct{}{}
   497  
   498  	byUpperCase := make(map[string]int)
   499  	for i := 0; i < t.NumField(); i++ {
   500  		sf := t.Field(i)
   501  
   502  		// Check that the yaml tag does not contain an _.
   503  		yamlTag := sf.Tag.Get("yaml")
   504  		if strings.Contains(yamlTag, "_") {
   505  			c.Fatalf("yaml field name includes _ character: %s", yamlTag)
   506  		}
   507  		upper := strings.ToUpper(sf.Name)
   508  		if _, present := byUpperCase[upper]; present {
   509  			c.Fatalf("field name collision in configuration object: %s", sf.Name)
   510  		}
   511  		byUpperCase[upper] = i
   512  
   513  		checkStructs(c, sf.Type, structsChecked)
   514  	}
   515  }
   516  
   517  // TestValidateConfigStruct makes sure that the config struct has no members
   518  // with yaml tags that would be ambiguous to the environment variable parser.
   519  func (suite *ConfigSuite) TestValidateConfigStruct(c *C) {
   520  	structsChecked := make(map[string]struct{})
   521  	checkStructs(c, reflect.TypeOf(Configuration{}), structsChecked)
   522  }
   523  
   524  func copyConfig(config Configuration) *Configuration {
   525  	configCopy := new(Configuration)
   526  
   527  	configCopy.Version = MajorMinorVersion(config.Version.Major(), config.Version.Minor())
   528  	configCopy.Loglevel = config.Loglevel
   529  	configCopy.Log = config.Log
   530  	configCopy.Catalog = config.Catalog
   531  	configCopy.Log.Fields = make(map[string]interface{}, len(config.Log.Fields))
   532  	for k, v := range config.Log.Fields {
   533  		configCopy.Log.Fields[k] = v
   534  	}
   535  
   536  	configCopy.Storage = Storage{config.Storage.Type(): Parameters{}}
   537  	for k, v := range config.Storage.Parameters() {
   538  		configCopy.Storage.setParameter(k, v)
   539  	}
   540  	configCopy.Reporting = Reporting{
   541  		Bugsnag:  BugsnagReporting{config.Reporting.Bugsnag.APIKey, config.Reporting.Bugsnag.ReleaseStage, config.Reporting.Bugsnag.Endpoint},
   542  		NewRelic: NewRelicReporting{config.Reporting.NewRelic.LicenseKey, config.Reporting.NewRelic.Name, config.Reporting.NewRelic.Verbose},
   543  	}
   544  
   545  	configCopy.Auth = Auth{config.Auth.Type(): Parameters{}}
   546  	for k, v := range config.Auth.Parameters() {
   547  		configCopy.Auth.setParameter(k, v)
   548  	}
   549  
   550  	configCopy.Notifications = Notifications{Endpoints: []Endpoint{}}
   551  	configCopy.Notifications.Endpoints = append(configCopy.Notifications.Endpoints, config.Notifications.Endpoints...)
   552  
   553  	configCopy.HTTP.Headers = make(http.Header)
   554  	for k, v := range config.HTTP.Headers {
   555  		configCopy.HTTP.Headers[k] = v
   556  	}
   557  
   558  	return configCopy
   559  }
   560  

View as plain text