github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+incompatible/configuration/configuration.go (about) 1 package configuration 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "net/http" 8 "reflect" 9 "strings" 10 "time" 11 ) 12 13 // Configuration is a versioned registry configuration, intended to be provided by a yaml file, and 14 // optionally modified by environment variables. 15 // 16 // Note that yaml field names should never include _ characters, since this is the separator used 17 // in environment variable names. 18 type Configuration struct { 19 // Version is the version which defines the format of the rest of the configuration 20 Version Version `yaml:"version"` 21 22 // Log supports setting various parameters related to the logging 23 // subsystem. 24 Log struct { 25 // Level is the granularity at which registry operations are logged. 26 Level Loglevel `yaml:"level"` 27 28 // Formatter overrides the default formatter with another. Options 29 // include "text", "json" and "logstash". 30 Formatter string `yaml:"formatter,omitempty"` 31 32 // Fields allows users to specify static string fields to include in 33 // the logger context. 34 Fields map[string]interface{} `yaml:"fields,omitempty"` 35 36 // Hooks allows users to configurate the log hooks, to enabling the 37 // sequent handling behavior, when defined levels of log message emit. 38 Hooks []LogHook `yaml:"hooks,omitempty"` 39 } 40 41 // Loglevel is the level at which registry operations are logged. This is 42 // deprecated. Please use Log.Level in the future. 43 Loglevel Loglevel `yaml:"loglevel,omitempty"` 44 45 // Storage is the configuration for the registry's storage driver 46 Storage Storage `yaml:"storage"` 47 48 // Auth allows configuration of various authorization methods that may be 49 // used to gate requests. 50 Auth Auth `yaml:"auth,omitempty"` 51 52 // Middleware lists all middlewares to be used by the registry. 53 Middleware map[string][]Middleware `yaml:"middleware,omitempty"` 54 55 // Reporting is the configuration for error reporting 56 Reporting Reporting `yaml:"reporting,omitempty"` 57 58 // HTTP contains configuration parameters for the registry's http 59 // interface. 60 HTTP struct { 61 // Addr specifies the bind address for the registry instance. 62 Addr string `yaml:"addr,omitempty"` 63 64 // Net specifies the net portion of the bind address. A default empty value means tcp. 65 Net string `yaml:"net,omitempty"` 66 67 // Host specifies an externally-reachable address for the registry, as a fully 68 // qualified URL. 69 Host string `yaml:"host,omitempty"` 70 71 Prefix string `yaml:"prefix,omitempty"` 72 73 // Secret specifies the secret key which HMAC tokens are created with. 74 Secret string `yaml:"secret,omitempty"` 75 76 // TLS instructs the http server to listen with a TLS configuration. 77 // This only support simple tls configuration with a cert and key. 78 // Mostly, this is useful for testing situations or simple deployments 79 // that require tls. If more complex configurations are required, use 80 // a proxy or make a proposal to add support here. 81 TLS struct { 82 // Certificate specifies the path to an x509 certificate file to 83 // be used for TLS. 84 Certificate string `yaml:"certificate,omitempty"` 85 86 // Key specifies the path to the x509 key file, which should 87 // contain the private portion for the file specified in 88 // Certificate. 89 Key string `yaml:"key,omitempty"` 90 91 // Specifies the CA certs for client authentication 92 // A file may contain multiple CA certificates encoded as PEM 93 ClientCAs []string `yaml:"clientcas,omitempty"` 94 } `yaml:"tls,omitempty"` 95 96 // Headers is a set of headers to include in HTTP responses. A common 97 // use case for this would be security headers such as 98 // Strict-Transport-Security. The map keys are the header names, and 99 // the values are the associated header payloads. 100 Headers http.Header `yaml:"headers,omitempty"` 101 102 // Debug configures the http debug interface, if specified. This can 103 // include services such as pprof, expvar and other data that should 104 // not be exposed externally. Left disabled by default. 105 Debug struct { 106 // Addr specifies the bind address for the debug server. 107 Addr string `yaml:"addr,omitempty"` 108 } `yaml:"debug,omitempty"` 109 } `yaml:"http,omitempty"` 110 111 // Notifications specifies configuration about various endpoint to which 112 // registry events are dispatched. 113 Notifications Notifications `yaml:"notifications,omitempty"` 114 115 // Redis configures the redis pool available to the registry webapp. 116 Redis struct { 117 // Addr specifies the the redis instance available to the application. 118 Addr string `yaml:"addr,omitempty"` 119 120 // Password string to use when making a connection. 121 Password string `yaml:"password,omitempty"` 122 123 // DB specifies the database to connect to on the redis instance. 124 DB int `yaml:"db,omitempty"` 125 126 DialTimeout time.Duration `yaml:"dialtimeout,omitempty"` // timeout for connect 127 ReadTimeout time.Duration `yaml:"readtimeout,omitempty"` // timeout for reads of data 128 WriteTimeout time.Duration `yaml:"writetimeout,omitempty"` // timeout for writes of data 129 130 // Pool configures the behavior of the redis connection pool. 131 Pool struct { 132 // MaxIdle sets the maximum number of idle connections. 133 MaxIdle int `yaml:"maxidle,omitempty"` 134 135 // MaxActive sets the maximum number of connections that should be 136 // opened before blocking a connection request. 137 MaxActive int `yaml:"maxactive,omitempty"` 138 139 // IdleTimeout sets the amount time to wait before closing 140 // inactive connections. 141 IdleTimeout time.Duration `yaml:"idletimeout,omitempty"` 142 } `yaml:"pool,omitempty"` 143 } `yaml:"redis,omitempty"` 144 145 Health Health `yaml:"health,omitempty"` 146 147 Proxy Proxy `yaml:"proxy,omitempty"` 148 } 149 150 // LogHook is composed of hook Level and Type. 151 // After hooks configuration, it can execute the next handling automatically, 152 // when defined levels of log message emitted. 153 // Example: hook can sending an email notification when error log happens in app. 154 type LogHook struct { 155 // Disable lets user select to enable hook or not. 156 Disabled bool `yaml:"disabled,omitempty"` 157 158 // Type allows user to select which type of hook handler they want. 159 Type string `yaml:"type,omitempty"` 160 161 // Levels set which levels of log message will let hook executed. 162 Levels []string `yaml:"levels,omitempty"` 163 164 // MailOptions allows user to configurate email parameters. 165 MailOptions MailOptions `yaml:"options,omitempty"` 166 } 167 168 // MailOptions provides the configuration sections to user, for specific handler. 169 type MailOptions struct { 170 SMTP struct { 171 // Addr defines smtp host address 172 Addr string `yaml:"addr,omitempty"` 173 174 // Username defines user name to smtp host 175 Username string `yaml:"username,omitempty"` 176 177 // Password defines password of login user 178 Password string `yaml:"password,omitempty"` 179 180 // Insecure defines if smtp login skips the secure cerification. 181 Insecure bool `yaml:"insecure,omitempty"` 182 } `yaml:"smtp,omitempty"` 183 184 // From defines mail sending address 185 From string `yaml:"from,omitempty"` 186 187 // To defines mail receiving address 188 To []string `yaml:"to,omitempty"` 189 } 190 191 // FileChecker is a type of entry in the health section for checking files. 192 type FileChecker struct { 193 // Interval is the duration in between checks 194 Interval time.Duration `yaml:"interval,omitempty"` 195 // File is the path to check 196 File string `yaml:"file,omitempty"` 197 // Threshold is the number of times a check must fail to trigger an 198 // unhealthy state 199 Threshold int `yaml:"threshold,omitempty"` 200 } 201 202 // HTTPChecker is a type of entry in the health section for checking HTTP URIs. 203 type HTTPChecker struct { 204 // Timeout is the duration to wait before timing out the HTTP request 205 Timeout time.Duration `yaml:"interval,omitempty"` 206 // StatusCode is the expected status code 207 StatusCode int 208 // Interval is the duration in between checks 209 Interval time.Duration `yaml:"interval,omitempty"` 210 // URI is the HTTP URI to check 211 URI string `yaml:"uri,omitempty"` 212 // Headers lists static headers that should be added to all requests 213 Headers http.Header `yaml:"headers"` 214 // Threshold is the number of times a check must fail to trigger an 215 // unhealthy state 216 Threshold int `yaml:"threshold,omitempty"` 217 } 218 219 // TCPChecker is a type of entry in the health section for checking TCP servers. 220 type TCPChecker struct { 221 // Timeout is the duration to wait before timing out the TCP connection 222 Timeout time.Duration `yaml:"interval,omitempty"` 223 // Interval is the duration in between checks 224 Interval time.Duration `yaml:"interval,omitempty"` 225 // Addr is the TCP address to check 226 Addr string `yaml:"addr,omitempty"` 227 // Threshold is the number of times a check must fail to trigger an 228 // unhealthy state 229 Threshold int `yaml:"threshold,omitempty"` 230 } 231 232 // Health provides the configuration section for health checks. 233 type Health struct { 234 // FileCheckers is a list of paths to check 235 FileCheckers []FileChecker `yaml:"file,omitempty"` 236 // HTTPCheckers is a list of URIs to check 237 HTTPCheckers []HTTPChecker `yaml:"http,omitempty"` 238 // TCPCheckers is a list of URIs to check 239 TCPCheckers []TCPChecker `yaml:"tcp,omitempty"` 240 // StorageDriver configures a health check on the configured storage 241 // driver 242 StorageDriver struct { 243 // Enabled turns on the health check for the storage driver 244 Enabled bool `yaml:"enabled,omitempty"` 245 // Interval is the duration in between checks 246 Interval time.Duration `yaml:"interval,omitempty"` 247 // Threshold is the number of times a check must fail to trigger an 248 // unhealthy state 249 Threshold int `yaml:"threshold,omitempty"` 250 } `yaml:"storagedriver,omitempty"` 251 } 252 253 // v0_1Configuration is a Version 0.1 Configuration struct 254 // This is currently aliased to Configuration, as it is the current version 255 type v0_1Configuration Configuration 256 257 // UnmarshalYAML implements the yaml.Unmarshaler interface 258 // Unmarshals a string of the form X.Y into a Version, validating that X and Y can represent uints 259 func (version *Version) UnmarshalYAML(unmarshal func(interface{}) error) error { 260 var versionString string 261 err := unmarshal(&versionString) 262 if err != nil { 263 return err 264 } 265 266 newVersion := Version(versionString) 267 if _, err := newVersion.major(); err != nil { 268 return err 269 } 270 271 if _, err := newVersion.minor(); err != nil { 272 return err 273 } 274 275 *version = newVersion 276 return nil 277 } 278 279 // CurrentVersion is the most recent Version that can be parsed 280 var CurrentVersion = MajorMinorVersion(0, 1) 281 282 // Loglevel is the level at which operations are logged 283 // This can be error, warn, info, or debug 284 type Loglevel string 285 286 // UnmarshalYAML implements the yaml.Umarshaler interface 287 // Unmarshals a string into a Loglevel, lowercasing the string and validating that it represents a 288 // valid loglevel 289 func (loglevel *Loglevel) UnmarshalYAML(unmarshal func(interface{}) error) error { 290 var loglevelString string 291 err := unmarshal(&loglevelString) 292 if err != nil { 293 return err 294 } 295 296 loglevelString = strings.ToLower(loglevelString) 297 switch loglevelString { 298 case "error", "warn", "info", "debug": 299 default: 300 return fmt.Errorf("Invalid loglevel %s Must be one of [error, warn, info, debug]", loglevelString) 301 } 302 303 *loglevel = Loglevel(loglevelString) 304 return nil 305 } 306 307 // Parameters defines a key-value parameters mapping 308 type Parameters map[string]interface{} 309 310 // Storage defines the configuration for registry object storage 311 type Storage map[string]Parameters 312 313 // Type returns the storage driver type, such as filesystem or s3 314 func (storage Storage) Type() string { 315 var storageType []string 316 317 // Return only key in this map 318 for k := range storage { 319 switch k { 320 case "maintenance": 321 // allow configuration of maintenance 322 case "cache": 323 // allow configuration of caching 324 case "delete": 325 // allow configuration of delete 326 case "redirect": 327 // allow configuration of redirect 328 default: 329 storageType = append(storageType, k) 330 } 331 } 332 if len(storageType) > 1 { 333 panic("multiple storage drivers specified in configuration or environment: " + strings.Join(storageType, ", ")) 334 } 335 if len(storageType) == 1 { 336 return storageType[0] 337 } 338 return "" 339 } 340 341 // Parameters returns the Parameters map for a Storage configuration 342 func (storage Storage) Parameters() Parameters { 343 return storage[storage.Type()] 344 } 345 346 // setParameter changes the parameter at the provided key to the new value 347 func (storage Storage) setParameter(key string, value interface{}) { 348 storage[storage.Type()][key] = value 349 } 350 351 // UnmarshalYAML implements the yaml.Unmarshaler interface 352 // Unmarshals a single item map into a Storage or a string into a Storage type with no parameters 353 func (storage *Storage) UnmarshalYAML(unmarshal func(interface{}) error) error { 354 var storageMap map[string]Parameters 355 err := unmarshal(&storageMap) 356 if err == nil { 357 if len(storageMap) > 1 { 358 types := make([]string, 0, len(storageMap)) 359 for k := range storageMap { 360 switch k { 361 case "maintenance": 362 // allow for configuration of maintenance 363 case "cache": 364 // allow configuration of caching 365 case "delete": 366 // allow configuration of delete 367 case "redirect": 368 // allow configuration of redirect 369 default: 370 types = append(types, k) 371 } 372 } 373 374 if len(types) > 1 { 375 return fmt.Errorf("Must provide exactly one storage type. Provided: %v", types) 376 } 377 } 378 *storage = storageMap 379 return nil 380 } 381 382 var storageType string 383 err = unmarshal(&storageType) 384 if err == nil { 385 *storage = Storage{storageType: Parameters{}} 386 return nil 387 } 388 389 return err 390 } 391 392 // MarshalYAML implements the yaml.Marshaler interface 393 func (storage Storage) MarshalYAML() (interface{}, error) { 394 if storage.Parameters() == nil { 395 return storage.Type(), nil 396 } 397 return map[string]Parameters(storage), nil 398 } 399 400 // Auth defines the configuration for registry authorization. 401 type Auth map[string]Parameters 402 403 // Type returns the storage driver type, such as filesystem or s3 404 func (auth Auth) Type() string { 405 // Return only key in this map 406 for k := range auth { 407 return k 408 } 409 return "" 410 } 411 412 // Parameters returns the Parameters map for an Auth configuration 413 func (auth Auth) Parameters() Parameters { 414 return auth[auth.Type()] 415 } 416 417 // setParameter changes the parameter at the provided key to the new value 418 func (auth Auth) setParameter(key string, value interface{}) { 419 auth[auth.Type()][key] = value 420 } 421 422 // UnmarshalYAML implements the yaml.Unmarshaler interface 423 // Unmarshals a single item map into a Storage or a string into a Storage type with no parameters 424 func (auth *Auth) UnmarshalYAML(unmarshal func(interface{}) error) error { 425 var m map[string]Parameters 426 err := unmarshal(&m) 427 if err == nil { 428 if len(m) > 1 { 429 types := make([]string, 0, len(m)) 430 for k := range m { 431 types = append(types, k) 432 } 433 434 // TODO(stevvooe): May want to change this slightly for 435 // authorization to allow multiple challenges. 436 return fmt.Errorf("must provide exactly one type. Provided: %v", types) 437 438 } 439 *auth = m 440 return nil 441 } 442 443 var authType string 444 err = unmarshal(&authType) 445 if err == nil { 446 *auth = Auth{authType: Parameters{}} 447 return nil 448 } 449 450 return err 451 } 452 453 // MarshalYAML implements the yaml.Marshaler interface 454 func (auth Auth) MarshalYAML() (interface{}, error) { 455 if auth.Parameters() == nil { 456 return auth.Type(), nil 457 } 458 return map[string]Parameters(auth), nil 459 } 460 461 // Notifications configures multiple http endpoints. 462 type Notifications struct { 463 // Endpoints is a list of http configurations for endpoints that 464 // respond to webhook notifications. In the future, we may allow other 465 // kinds of endpoints, such as external queues. 466 Endpoints []Endpoint `yaml:"endpoints,omitempty"` 467 } 468 469 // Endpoint describes the configuration of an http webhook notification 470 // endpoint. 471 type Endpoint struct { 472 Name string `yaml:"name"` // identifies the endpoint in the registry instance. 473 Disabled bool `yaml:"disabled"` // disables the endpoint 474 URL string `yaml:"url"` // post url for the endpoint. 475 Headers http.Header `yaml:"headers"` // static headers that should be added to all requests 476 Timeout time.Duration `yaml:"timeout"` // HTTP timeout 477 Threshold int `yaml:"threshold"` // circuit breaker threshold before backing off on failure 478 Backoff time.Duration `yaml:"backoff"` // backoff duration 479 } 480 481 // Reporting defines error reporting methods. 482 type Reporting struct { 483 // Bugsnag configures error reporting for Bugsnag (bugsnag.com). 484 Bugsnag BugsnagReporting `yaml:"bugsnag,omitempty"` 485 // NewRelic configures error reporting for NewRelic (newrelic.com) 486 NewRelic NewRelicReporting `yaml:"newrelic,omitempty"` 487 } 488 489 // BugsnagReporting configures error reporting for Bugsnag (bugsnag.com). 490 type BugsnagReporting struct { 491 // APIKey is the Bugsnag api key. 492 APIKey string `yaml:"apikey,omitempty"` 493 // ReleaseStage tracks where the registry is deployed. 494 // Examples: production, staging, development 495 ReleaseStage string `yaml:"releasestage,omitempty"` 496 // Endpoint is used for specifying an enterprise Bugsnag endpoint. 497 Endpoint string `yaml:"endpoint,omitempty"` 498 } 499 500 // NewRelicReporting configures error reporting for NewRelic (newrelic.com) 501 type NewRelicReporting struct { 502 // LicenseKey is the NewRelic user license key 503 LicenseKey string `yaml:"licensekey,omitempty"` 504 // Name is the component name of the registry in NewRelic 505 Name string `yaml:"name,omitempty"` 506 // Verbose configures debug output to STDOUT 507 Verbose bool `yaml:"verbose,omitempty"` 508 } 509 510 // Middleware configures named middlewares to be applied at injection points. 511 type Middleware struct { 512 // Name the middleware registers itself as 513 Name string `yaml:"name"` 514 // Flag to disable middleware easily 515 Disabled bool `yaml:"disabled,omitempty"` 516 // Map of parameters that will be passed to the middleware's initialization function 517 Options Parameters `yaml:"options"` 518 } 519 520 // Proxy configures the registry as a pull through cache 521 type Proxy struct { 522 // RemoteURL is the URL of the remote registry 523 RemoteURL string `yaml:"remoteurl"` 524 525 // Username of the hub user 526 Username string `yaml:"username"` 527 528 // Password of the hub user 529 Password string `yaml:"password"` 530 } 531 532 // Parse parses an input configuration yaml document into a Configuration struct 533 // This should generally be capable of handling old configuration format versions 534 // 535 // Environment variables may be used to override configuration parameters other than version, 536 // following the scheme below: 537 // Configuration.Abc may be replaced by the value of REGISTRY_ABC, 538 // Configuration.Abc.Xyz may be replaced by the value of REGISTRY_ABC_XYZ, and so forth 539 func Parse(rd io.Reader) (*Configuration, error) { 540 in, err := ioutil.ReadAll(rd) 541 if err != nil { 542 return nil, err 543 } 544 545 p := NewParser("registry", []VersionedParseInfo{ 546 { 547 Version: MajorMinorVersion(0, 1), 548 ParseAs: reflect.TypeOf(v0_1Configuration{}), 549 ConversionFunc: func(c interface{}) (interface{}, error) { 550 if v0_1, ok := c.(*v0_1Configuration); ok { 551 if v0_1.Loglevel == Loglevel("") { 552 v0_1.Loglevel = Loglevel("info") 553 } 554 if v0_1.Storage.Type() == "" { 555 return nil, fmt.Errorf("No storage configuration provided") 556 } 557 return (*Configuration)(v0_1), nil 558 } 559 return nil, fmt.Errorf("Expected *v0_1Configuration, received %#v", c) 560 }, 561 }, 562 }) 563 564 config := new(Configuration) 565 err = p.Parse(in, config) 566 if err != nil { 567 return nil, err 568 } 569 570 return config, nil 571 }