1 package configuration 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "net/http" 9 "reflect" 10 "strings" 11 "time" 12 ) 13 14 // Configuration is a versioned registry configuration, intended to be provided by a yaml file, and 15 // optionally modified by environment variables. 16 // 17 // Note that yaml field names should never include _ characters, since this is the separator used 18 // in environment variable names. 19 type Configuration struct { 20 // Version is the version which defines the format of the rest of the configuration 21 Version Version `yaml:"version"` 22 23 // Log supports setting various parameters related to the logging 24 // subsystem. 25 Log struct { 26 // AccessLog configures access logging. 27 AccessLog struct { 28 // Disabled disables access logging. 29 Disabled bool `yaml:"disabled,omitempty"` 30 } `yaml:"accesslog,omitempty"` 31 32 // Level is the granularity at which registry operations are logged. 33 Level Loglevel `yaml:"level,omitempty"` 34 35 // Formatter overrides the default formatter with another. Options 36 // include "text", "json" and "logstash". 37 Formatter string `yaml:"formatter,omitempty"` 38 39 // Fields allows users to specify static string fields to include in 40 // the logger context. 41 Fields map[string]interface{} `yaml:"fields,omitempty"` 42 43 // Hooks allows users to configure the log hooks, to enabling the 44 // sequent handling behavior, when defined levels of log message emit. 45 Hooks []LogHook `yaml:"hooks,omitempty"` 46 } 47 48 // Loglevel is the level at which registry operations are logged. 49 // 50 // Deprecated: Use Log.Level instead. 51 Loglevel Loglevel `yaml:"loglevel,omitempty"` 52 53 // Storage is the configuration for the registry's storage driver 54 Storage Storage `yaml:"storage"` 55 56 // Auth allows configuration of various authorization methods that may be 57 // used to gate requests. 58 Auth Auth `yaml:"auth,omitempty"` 59 60 // Middleware lists all middlewares to be used by the registry. 61 Middleware map[string][]Middleware `yaml:"middleware,omitempty"` 62 63 // Reporting is the configuration for error reporting 64 Reporting Reporting `yaml:"reporting,omitempty"` 65 66 // HTTP contains configuration parameters for the registry's http 67 // interface. 68 HTTP struct { 69 // Addr specifies the bind address for the registry instance. 70 Addr string `yaml:"addr,omitempty"` 71 72 // Net specifies the net portion of the bind address. A default empty value means tcp. 73 Net string `yaml:"net,omitempty"` 74 75 // Host specifies an externally-reachable address for the registry, as a fully 76 // qualified URL. 77 Host string `yaml:"host,omitempty"` 78 79 Prefix string `yaml:"prefix,omitempty"` 80 81 // Secret specifies the secret key which HMAC tokens are created with. 82 Secret string `yaml:"secret,omitempty"` 83 84 // RelativeURLs specifies that relative URLs should be returned in 85 // Location headers 86 RelativeURLs bool `yaml:"relativeurls,omitempty"` 87 88 // Amount of time to wait for connection to drain before shutting down when registry 89 // receives a stop signal 90 DrainTimeout time.Duration `yaml:"draintimeout,omitempty"` 91 92 // TLS instructs the http server to listen with a TLS configuration. 93 // This only support simple tls configuration with a cert and key. 94 // Mostly, this is useful for testing situations or simple deployments 95 // that require tls. If more complex configurations are required, use 96 // a proxy or make a proposal to add support here. 97 TLS struct { 98 // Certificate specifies the path to an x509 certificate file to 99 // be used for TLS. 100 Certificate string `yaml:"certificate,omitempty"` 101 102 // Key specifies the path to the x509 key file, which should 103 // contain the private portion for the file specified in 104 // Certificate. 105 Key string `yaml:"key,omitempty"` 106 107 // Specifies the CA certs for client authentication 108 // A file may contain multiple CA certificates encoded as PEM 109 ClientCAs []string `yaml:"clientcas,omitempty"` 110 111 // Specifies the lowest TLS version allowed 112 MinimumTLS string `yaml:"minimumtls,omitempty"` 113 114 // Specifies a list of cipher suites allowed 115 CipherSuites []string `yaml:"ciphersuites,omitempty"` 116 117 // LetsEncrypt is used to configuration setting up TLS through 118 // Let's Encrypt instead of manually specifying certificate and 119 // key. If a TLS certificate is specified, the Let's Encrypt 120 // section will not be used. 121 LetsEncrypt struct { 122 // CacheFile specifies cache file to use for lets encrypt 123 // certificates and keys. 124 CacheFile string `yaml:"cachefile,omitempty"` 125 126 // Email is the email to use during Let's Encrypt registration 127 Email string `yaml:"email,omitempty"` 128 129 // Hosts specifies the hosts which are allowed to obtain Let's 130 // Encrypt certificates. 131 Hosts []string `yaml:"hosts,omitempty"` 132 } `yaml:"letsencrypt,omitempty"` 133 } `yaml:"tls,omitempty"` 134 135 // Headers is a set of headers to include in HTTP responses. A common 136 // use case for this would be security headers such as 137 // Strict-Transport-Security. The map keys are the header names, and 138 // the values are the associated header payloads. 139 Headers http.Header `yaml:"headers,omitempty"` 140 141 // Debug configures the http debug interface, if specified. This can 142 // include services such as pprof, expvar and other data that should 143 // not be exposed externally. Left disabled by default. 144 Debug struct { 145 // Addr specifies the bind address for the debug server. 146 Addr string `yaml:"addr,omitempty"` 147 // Prometheus configures the Prometheus telemetry endpoint. 148 Prometheus struct { 149 Enabled bool `yaml:"enabled,omitempty"` 150 Path string `yaml:"path,omitempty"` 151 } `yaml:"prometheus,omitempty"` 152 } `yaml:"debug,omitempty"` 153 154 // HTTP2 configuration options 155 HTTP2 struct { 156 // Specifies whether the registry should disallow clients attempting 157 // to connect via http2. If set to true, only http/1.1 is supported. 158 Disabled bool `yaml:"disabled,omitempty"` 159 } `yaml:"http2,omitempty"` 160 } `yaml:"http,omitempty"` 161 162 // Notifications specifies configuration about various endpoint to which 163 // registry events are dispatched. 164 Notifications Notifications `yaml:"notifications,omitempty"` 165 166 // Redis configures the redis pool available to the registry webapp. 167 Redis struct { 168 // Addr specifies the the redis instance available to the application. 169 Addr string `yaml:"addr,omitempty"` 170 171 // Password string to use when making a connection. 172 Password string `yaml:"password,omitempty"` 173 174 // DB specifies the database to connect to on the redis instance. 175 DB int `yaml:"db,omitempty"` 176 177 DialTimeout time.Duration `yaml:"dialtimeout,omitempty"` // timeout for connect 178 ReadTimeout time.Duration `yaml:"readtimeout,omitempty"` // timeout for reads of data 179 WriteTimeout time.Duration `yaml:"writetimeout,omitempty"` // timeout for writes of data 180 181 // Pool configures the behavior of the redis connection pool. 182 Pool struct { 183 // MaxIdle sets the maximum number of idle connections. 184 MaxIdle int `yaml:"maxidle,omitempty"` 185 186 // MaxActive sets the maximum number of connections that should be 187 // opened before blocking a connection request. 188 MaxActive int `yaml:"maxactive,omitempty"` 189 190 // IdleTimeout sets the amount time to wait before closing 191 // inactive connections. 192 IdleTimeout time.Duration `yaml:"idletimeout,omitempty"` 193 } `yaml:"pool,omitempty"` 194 } `yaml:"redis,omitempty"` 195 196 Health Health `yaml:"health,omitempty"` 197 Catalog Catalog `yaml:"catalog,omitempty"` 198 199 Proxy Proxy `yaml:"proxy,omitempty"` 200 201 // Compatibility is used for configurations of working with older or deprecated features. 202 Compatibility struct { 203 // Schema1 configures how schema1 manifests will be handled 204 Schema1 struct { 205 // TrustKey is the signing key to use for adding the signature to 206 // schema1 manifests. 207 TrustKey string `yaml:"signingkeyfile,omitempty"` 208 // Enabled determines if schema1 manifests should be pullable 209 Enabled bool `yaml:"enabled,omitempty"` 210 } `yaml:"schema1,omitempty"` 211 } `yaml:"compatibility,omitempty"` 212 213 // Validation configures validation options for the registry. 214 Validation struct { 215 // Enabled enables the other options in this section. This field is 216 // deprecated in favor of Disabled. 217 Enabled bool `yaml:"enabled,omitempty"` 218 // Disabled disables the other options in this section. 219 Disabled bool `yaml:"disabled,omitempty"` 220 // Manifests configures manifest validation. 221 Manifests struct { 222 // URLs configures validation for URLs in pushed manifests. 223 URLs struct { 224 // Allow specifies regular expressions (https://godoc.org/regexp/syntax) 225 // that URLs in pushed manifests must match. 226 Allow []string `yaml:"allow,omitempty"` 227 // Deny specifies regular expressions (https://godoc.org/regexp/syntax) 228 // that URLs in pushed manifests must not match. 229 Deny []string `yaml:"deny,omitempty"` 230 } `yaml:"urls,omitempty"` 231 } `yaml:"manifests,omitempty"` 232 } `yaml:"validation,omitempty"` 233 234 // Policy configures registry policy options. 235 Policy struct { 236 // Repository configures policies for repositories 237 Repository struct { 238 // Classes is a list of repository classes which the 239 // registry allows content for. This class is matched 240 // against the configuration media type inside uploaded 241 // manifests. When non-empty, the registry will enforce 242 // the class in authorized resources. 243 Classes []string `yaml:"classes"` 244 } `yaml:"repository,omitempty"` 245 } `yaml:"policy,omitempty"` 246 } 247 248 // Catalog is composed of MaxEntries. 249 // Catalog endpoint (/v2/_catalog) configuration, it provides the configuration 250 // options to control the maximum number of entries returned by the catalog endpoint. 251 type Catalog struct { 252 // Max number of entries returned by the catalog endpoint. Requesting n entries 253 // to the catalog endpoint will return at most MaxEntries entries. 254 // An empty or a negative value will set a default of 1000 maximum entries by default. 255 MaxEntries int `yaml:"maxentries,omitempty"` 256 } 257 258 // LogHook is composed of hook Level and Type. 259 // After hooks configuration, it can execute the next handling automatically, 260 // when defined levels of log message emitted. 261 // Example: hook can sending an email notification when error log happens in app. 262 type LogHook struct { 263 // Disable lets user select to enable hook or not. 264 Disabled bool `yaml:"disabled,omitempty"` 265 266 // Type allows user to select which type of hook handler they want. 267 Type string `yaml:"type,omitempty"` 268 269 // Levels set which levels of log message will let hook executed. 270 Levels []string `yaml:"levels,omitempty"` 271 272 // MailOptions allows user to configure email parameters. 273 MailOptions MailOptions `yaml:"options,omitempty"` 274 } 275 276 // MailOptions provides the configuration sections to user, for specific handler. 277 type MailOptions struct { 278 SMTP struct { 279 // Addr defines smtp host address 280 Addr string `yaml:"addr,omitempty"` 281 282 // Username defines user name to smtp host 283 Username string `yaml:"username,omitempty"` 284 285 // Password defines password of login user 286 Password string `yaml:"password,omitempty"` 287 288 // Insecure defines if smtp login skips the secure certification. 289 Insecure bool `yaml:"insecure,omitempty"` 290 } `yaml:"smtp,omitempty"` 291 292 // From defines mail sending address 293 From string `yaml:"from,omitempty"` 294 295 // To defines mail receiving address 296 To []string `yaml:"to,omitempty"` 297 } 298 299 // FileChecker is a type of entry in the health section for checking files. 300 type FileChecker struct { 301 // Interval is the duration in between checks 302 Interval time.Duration `yaml:"interval,omitempty"` 303 // File is the path to check 304 File string `yaml:"file,omitempty"` 305 // Threshold is the number of times a check must fail to trigger an 306 // unhealthy state 307 Threshold int `yaml:"threshold,omitempty"` 308 } 309 310 // HTTPChecker is a type of entry in the health section for checking HTTP URIs. 311 type HTTPChecker struct { 312 // Timeout is the duration to wait before timing out the HTTP request 313 Timeout time.Duration `yaml:"timeout,omitempty"` 314 // StatusCode is the expected status code 315 StatusCode int 316 // Interval is the duration in between checks 317 Interval time.Duration `yaml:"interval,omitempty"` 318 // URI is the HTTP URI to check 319 URI string `yaml:"uri,omitempty"` 320 // Headers lists static headers that should be added to all requests 321 Headers http.Header `yaml:"headers"` 322 // Threshold is the number of times a check must fail to trigger an 323 // unhealthy state 324 Threshold int `yaml:"threshold,omitempty"` 325 } 326 327 // TCPChecker is a type of entry in the health section for checking TCP servers. 328 type TCPChecker struct { 329 // Timeout is the duration to wait before timing out the TCP connection 330 Timeout time.Duration `yaml:"timeout,omitempty"` 331 // Interval is the duration in between checks 332 Interval time.Duration `yaml:"interval,omitempty"` 333 // Addr is the TCP address to check 334 Addr string `yaml:"addr,omitempty"` 335 // Threshold is the number of times a check must fail to trigger an 336 // unhealthy state 337 Threshold int `yaml:"threshold,omitempty"` 338 } 339 340 // Health provides the configuration section for health checks. 341 type Health struct { 342 // FileCheckers is a list of paths to check 343 FileCheckers []FileChecker `yaml:"file,omitempty"` 344 // HTTPCheckers is a list of URIs to check 345 HTTPCheckers []HTTPChecker `yaml:"http,omitempty"` 346 // TCPCheckers is a list of URIs to check 347 TCPCheckers []TCPChecker `yaml:"tcp,omitempty"` 348 // StorageDriver configures a health check on the configured storage 349 // driver 350 StorageDriver struct { 351 // Enabled turns on the health check for the storage driver 352 Enabled bool `yaml:"enabled,omitempty"` 353 // Interval is the duration in between checks 354 Interval time.Duration `yaml:"interval,omitempty"` 355 // Threshold is the number of times a check must fail to trigger an 356 // unhealthy state 357 Threshold int `yaml:"threshold,omitempty"` 358 } `yaml:"storagedriver,omitempty"` 359 } 360 361 // v0_1Configuration is a Version 0.1 Configuration struct 362 // This is currently aliased to Configuration, as it is the current version 363 type v0_1Configuration Configuration 364 365 // UnmarshalYAML implements the yaml.Unmarshaler interface 366 // Unmarshals a string of the form X.Y into a Version, validating that X and Y can represent unsigned integers 367 func (version *Version) UnmarshalYAML(unmarshal func(interface{}) error) error { 368 var versionString string 369 err := unmarshal(&versionString) 370 if err != nil { 371 return err 372 } 373 374 newVersion := Version(versionString) 375 if _, err := newVersion.major(); err != nil { 376 return err 377 } 378 379 if _, err := newVersion.minor(); err != nil { 380 return err 381 } 382 383 *version = newVersion 384 return nil 385 } 386 387 // CurrentVersion is the most recent Version that can be parsed 388 var CurrentVersion = MajorMinorVersion(0, 1) 389 390 // Loglevel is the level at which operations are logged 391 // This can be error, warn, info, or debug 392 type Loglevel string 393 394 // UnmarshalYAML implements the yaml.Umarshaler interface 395 // Unmarshals a string into a Loglevel, lowercasing the string and validating that it represents a 396 // valid loglevel 397 func (loglevel *Loglevel) UnmarshalYAML(unmarshal func(interface{}) error) error { 398 var loglevelString string 399 err := unmarshal(&loglevelString) 400 if err != nil { 401 return err 402 } 403 404 loglevelString = strings.ToLower(loglevelString) 405 switch loglevelString { 406 case "error", "warn", "info", "debug": 407 default: 408 return fmt.Errorf("invalid loglevel %s Must be one of [error, warn, info, debug]", loglevelString) 409 } 410 411 *loglevel = Loglevel(loglevelString) 412 return nil 413 } 414 415 // Parameters defines a key-value parameters mapping 416 type Parameters map[string]interface{} 417 418 // Storage defines the configuration for registry object storage 419 type Storage map[string]Parameters 420 421 // Type returns the storage driver type, such as filesystem or s3 422 func (storage Storage) Type() string { 423 var storageType []string 424 425 // Return only key in this map 426 for k := range storage { 427 switch k { 428 case "maintenance": 429 // allow configuration of maintenance 430 case "cache": 431 // allow configuration of caching 432 case "delete": 433 // allow configuration of delete 434 case "redirect": 435 // allow configuration of redirect 436 default: 437 storageType = append(storageType, k) 438 } 439 } 440 if len(storageType) > 1 { 441 panic("multiple storage drivers specified in configuration or environment: " + strings.Join(storageType, ", ")) 442 } 443 if len(storageType) == 1 { 444 return storageType[0] 445 } 446 return "" 447 } 448 449 // Parameters returns the Parameters map for a Storage configuration 450 func (storage Storage) Parameters() Parameters { 451 return storage[storage.Type()] 452 } 453 454 // setParameter changes the parameter at the provided key to the new value 455 func (storage Storage) setParameter(key string, value interface{}) { 456 storage[storage.Type()][key] = value 457 } 458 459 // UnmarshalYAML implements the yaml.Unmarshaler interface 460 // Unmarshals a single item map into a Storage or a string into a Storage type with no parameters 461 func (storage *Storage) UnmarshalYAML(unmarshal func(interface{}) error) error { 462 var storageMap map[string]Parameters 463 err := unmarshal(&storageMap) 464 if err == nil { 465 if len(storageMap) > 1 { 466 types := make([]string, 0, len(storageMap)) 467 for k := range storageMap { 468 switch k { 469 case "maintenance": 470 // allow for configuration of maintenance 471 case "cache": 472 // allow configuration of caching 473 case "delete": 474 // allow configuration of delete 475 case "redirect": 476 // allow configuration of redirect 477 default: 478 types = append(types, k) 479 } 480 } 481 482 if len(types) > 1 { 483 return fmt.Errorf("must provide exactly one storage type. Provided: %v", types) 484 } 485 } 486 *storage = storageMap 487 return nil 488 } 489 490 var storageType string 491 err = unmarshal(&storageType) 492 if err == nil { 493 *storage = Storage{storageType: Parameters{}} 494 return nil 495 } 496 497 return err 498 } 499 500 // MarshalYAML implements the yaml.Marshaler interface 501 func (storage Storage) MarshalYAML() (interface{}, error) { 502 if storage.Parameters() == nil { 503 return storage.Type(), nil 504 } 505 return map[string]Parameters(storage), nil 506 } 507 508 // Auth defines the configuration for registry authorization. 509 type Auth map[string]Parameters 510 511 // Type returns the auth type, such as htpasswd or token 512 func (auth Auth) Type() string { 513 // Return only key in this map 514 for k := range auth { 515 return k 516 } 517 return "" 518 } 519 520 // Parameters returns the Parameters map for an Auth configuration 521 func (auth Auth) Parameters() Parameters { 522 return auth[auth.Type()] 523 } 524 525 // setParameter changes the parameter at the provided key to the new value 526 func (auth Auth) setParameter(key string, value interface{}) { 527 auth[auth.Type()][key] = value 528 } 529 530 // UnmarshalYAML implements the yaml.Unmarshaler interface 531 // Unmarshals a single item map into a Storage or a string into a Storage type with no parameters 532 func (auth *Auth) UnmarshalYAML(unmarshal func(interface{}) error) error { 533 var m map[string]Parameters 534 err := unmarshal(&m) 535 if err == nil { 536 if len(m) > 1 { 537 types := make([]string, 0, len(m)) 538 for k := range m { 539 types = append(types, k) 540 } 541 542 // TODO(stevvooe): May want to change this slightly for 543 // authorization to allow multiple challenges. 544 return fmt.Errorf("must provide exactly one type. Provided: %v", types) 545 546 } 547 *auth = m 548 return nil 549 } 550 551 var authType string 552 err = unmarshal(&authType) 553 if err == nil { 554 *auth = Auth{authType: Parameters{}} 555 return nil 556 } 557 558 return err 559 } 560 561 // MarshalYAML implements the yaml.Marshaler interface 562 func (auth Auth) MarshalYAML() (interface{}, error) { 563 if auth.Parameters() == nil { 564 return auth.Type(), nil 565 } 566 return map[string]Parameters(auth), nil 567 } 568 569 // Notifications configures multiple http endpoints. 570 type Notifications struct { 571 // EventConfig is the configuration for the event format that is sent to each Endpoint. 572 EventConfig Events `yaml:"events,omitempty"` 573 // Endpoints is a list of http configurations for endpoints that 574 // respond to webhook notifications. In the future, we may allow other 575 // kinds of endpoints, such as external queues. 576 Endpoints []Endpoint `yaml:"endpoints,omitempty"` 577 } 578 579 // Endpoint describes the configuration of an http webhook notification 580 // endpoint. 581 type Endpoint struct { 582 Name string `yaml:"name"` // identifies the endpoint in the registry instance. 583 Disabled bool `yaml:"disabled"` // disables the endpoint 584 URL string `yaml:"url"` // post url for the endpoint. 585 Headers http.Header `yaml:"headers"` // static headers that should be added to all requests 586 Timeout time.Duration `yaml:"timeout"` // HTTP timeout 587 Threshold int `yaml:"threshold"` // circuit breaker threshold before backing off on failure 588 Backoff time.Duration `yaml:"backoff"` // backoff duration 589 IgnoredMediaTypes []string `yaml:"ignoredmediatypes"` // target media types to ignore 590 Ignore Ignore `yaml:"ignore"` // ignore event types 591 } 592 593 // Events configures notification events. 594 type Events struct { 595 IncludeReferences bool `yaml:"includereferences"` // include reference data in manifest events 596 } 597 598 // Ignore configures mediaTypes and actions of the event, that it won't be propagated 599 type Ignore struct { 600 MediaTypes []string `yaml:"mediatypes"` // target media types to ignore 601 Actions []string `yaml:"actions"` // ignore action types 602 } 603 604 // Reporting defines error reporting methods. 605 type Reporting struct { 606 // Bugsnag configures error reporting for Bugsnag (bugsnag.com). 607 Bugsnag BugsnagReporting `yaml:"bugsnag,omitempty"` 608 // NewRelic configures error reporting for NewRelic (newrelic.com) 609 NewRelic NewRelicReporting `yaml:"newrelic,omitempty"` 610 } 611 612 // BugsnagReporting configures error reporting for Bugsnag (bugsnag.com). 613 type BugsnagReporting struct { 614 // APIKey is the Bugsnag api key. 615 APIKey string `yaml:"apikey,omitempty"` 616 // ReleaseStage tracks where the registry is deployed. 617 // Examples: production, staging, development 618 ReleaseStage string `yaml:"releasestage,omitempty"` 619 // Endpoint is used for specifying an enterprise Bugsnag endpoint. 620 Endpoint string `yaml:"endpoint,omitempty"` 621 } 622 623 // NewRelicReporting configures error reporting for NewRelic (newrelic.com) 624 type NewRelicReporting struct { 625 // LicenseKey is the NewRelic user license key 626 LicenseKey string `yaml:"licensekey,omitempty"` 627 // Name is the component name of the registry in NewRelic 628 Name string `yaml:"name,omitempty"` 629 // Verbose configures debug output to STDOUT 630 Verbose bool `yaml:"verbose,omitempty"` 631 } 632 633 // Middleware configures named middlewares to be applied at injection points. 634 type Middleware struct { 635 // Name the middleware registers itself as 636 Name string `yaml:"name"` 637 // Flag to disable middleware easily 638 Disabled bool `yaml:"disabled,omitempty"` 639 // Map of parameters that will be passed to the middleware's initialization function 640 Options Parameters `yaml:"options"` 641 } 642 643 // Proxy configures the registry as a pull through cache 644 type Proxy struct { 645 // RemoteURL is the URL of the remote registry 646 RemoteURL string `yaml:"remoteurl"` 647 648 // Username of the hub user 649 Username string `yaml:"username"` 650 651 // Password of the hub user 652 Password string `yaml:"password"` 653 } 654 655 // Parse parses an input configuration yaml document into a Configuration struct 656 // This should generally be capable of handling old configuration format versions 657 // 658 // Environment variables may be used to override configuration parameters other than version, 659 // following the scheme below: 660 // Configuration.Abc may be replaced by the value of REGISTRY_ABC, 661 // Configuration.Abc.Xyz may be replaced by the value of REGISTRY_ABC_XYZ, and so forth 662 func Parse(rd io.Reader) (*Configuration, error) { 663 in, err := ioutil.ReadAll(rd) 664 if err != nil { 665 return nil, err 666 } 667 668 p := NewParser("registry", []VersionedParseInfo{ 669 { 670 Version: MajorMinorVersion(0, 1), 671 ParseAs: reflect.TypeOf(v0_1Configuration{}), 672 ConversionFunc: func(c interface{}) (interface{}, error) { 673 if v0_1, ok := c.(*v0_1Configuration); ok { 674 if v0_1.Log.Level == Loglevel("") { 675 if v0_1.Loglevel != Loglevel("") { 676 v0_1.Log.Level = v0_1.Loglevel 677 } else { 678 v0_1.Log.Level = Loglevel("info") 679 } 680 } 681 if v0_1.Loglevel != Loglevel("") { 682 v0_1.Loglevel = Loglevel("") 683 } 684 685 if v0_1.Catalog.MaxEntries <= 0 { 686 v0_1.Catalog.MaxEntries = 1000 687 } 688 689 if v0_1.Storage.Type() == "" { 690 return nil, errors.New("no storage configuration provided") 691 } 692 return (*Configuration)(v0_1), nil 693 } 694 return nil, fmt.Errorf("expected *v0_1Configuration, received %#v", c) 695 }, 696 }, 697 }) 698 699 config := new(Configuration) 700 err = p.Parse(in, config) 701 if err != nil { 702 return nil, err 703 } 704 705 return config, nil 706 } 707