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  }