Merge pull request #491 from noxiouz/unix_socket_listener
cmd/registry, registry/listener: listen and serve on a unix socketmaster
						commit
						7c2d0ee81d
					
				|  | @ -21,6 +21,7 @@ import ( | |||
| 	_ "github.com/docker/distribution/registry/auth/silly" | ||||
| 	_ "github.com/docker/distribution/registry/auth/token" | ||||
| 	"github.com/docker/distribution/registry/handlers" | ||||
| 	"github.com/docker/distribution/registry/listener" | ||||
| 	_ "github.com/docker/distribution/registry/storage/driver/azure" | ||||
| 	_ "github.com/docker/distribution/registry/storage/driver/filesystem" | ||||
| 	_ "github.com/docker/distribution/registry/storage/driver/inmemory" | ||||
|  | @ -67,14 +68,26 @@ func main() { | |||
| 		go debugServer(config.HTTP.Debug.Addr) | ||||
| 	} | ||||
| 
 | ||||
| 	if config.HTTP.TLS.Certificate == "" { | ||||
| 		context.GetLogger(app).Infof("listening on %v", config.HTTP.Addr) | ||||
| 		if err := http.ListenAndServe(config.HTTP.Addr, handler); err != nil { | ||||
| 			context.GetLogger(app).Fatalln(err) | ||||
| 		} | ||||
| 	} else { | ||||
| 	server := &http.Server{ | ||||
| 		Handler: handler, | ||||
| 	} | ||||
| 
 | ||||
| 	ln, err := listener.NewListener(config.HTTP.Net, config.HTTP.Addr) | ||||
| 	if err != nil { | ||||
| 		context.GetLogger(app).Fatalln(err) | ||||
| 	} | ||||
| 	defer ln.Close() | ||||
| 
 | ||||
| 	if config.HTTP.TLS.Certificate != "" { | ||||
| 		tlsConf := &tls.Config{ | ||||
| 			ClientAuth: tls.NoClientCert, | ||||
| 			ClientAuth:   tls.NoClientCert, | ||||
| 			NextProtos:   []string{"http/1.1"}, | ||||
| 			Certificates: make([]tls.Certificate, 1), | ||||
| 		} | ||||
| 
 | ||||
| 		tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key) | ||||
| 		if err != nil { | ||||
| 			context.GetLogger(app).Fatalln(err) | ||||
| 		} | ||||
| 
 | ||||
| 		if len(config.HTTP.TLS.ClientCAs) != 0 { | ||||
|  | @ -99,16 +112,14 @@ func main() { | |||
| 			tlsConf.ClientCAs = pool | ||||
| 		} | ||||
| 
 | ||||
| 		context.GetLogger(app).Infof("listening on %v, tls", config.HTTP.Addr) | ||||
| 		server := &http.Server{ | ||||
| 			Addr:      config.HTTP.Addr, | ||||
| 			Handler:   handler, | ||||
| 			TLSConfig: tlsConf, | ||||
| 		} | ||||
| 		ln = tls.NewListener(ln, tlsConf) | ||||
| 		context.GetLogger(app).Infof("listening on %v, tls", ln.Addr()) | ||||
| 	} else { | ||||
| 		context.GetLogger(app).Infof("listening on %v", ln.Addr()) | ||||
| 	} | ||||
| 
 | ||||
| 		if err := server.ListenAndServeTLS(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key); err != nil { | ||||
| 			context.GetLogger(app).Fatalln(err) | ||||
| 		} | ||||
| 	if err := server.Serve(ln); err != nil { | ||||
| 		context.GetLogger(app).Fatalln(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -54,6 +54,9 @@ type Configuration struct { | |||
| 		// Addr specifies the bind address for the registry instance.
 | ||||
| 		Addr string `yaml:"addr,omitempty"` | ||||
| 
 | ||||
| 		// Net specifies the net portion of the bind address. A default empty value means tcp.
 | ||||
| 		Net string `yaml:"net,omitempty"` | ||||
| 
 | ||||
| 		Prefix string `yaml:"prefix,omitempty"` | ||||
| 
 | ||||
| 		// Secret specifies the secret key which HMAC tokens are created with.
 | ||||
|  |  | |||
|  | @ -61,6 +61,7 @@ var configStruct = Configuration{ | |||
| 	}, | ||||
| 	HTTP: struct { | ||||
| 		Addr   string `yaml:"addr,omitempty"` | ||||
| 		Net    string `yaml:"net,omitempty"` | ||||
| 		Prefix string `yaml:"prefix,omitempty"` | ||||
| 		Secret string `yaml:"secret,omitempty"` | ||||
| 		TLS    struct { | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| <!--GITHUB | ||||
| page_title: Configure a Registry | ||||
| page_description: Explains how to deploy a registry  | ||||
| page_description: Explains how to deploy a registry | ||||
| page_keywords: registry, service, images, repository | ||||
| IGNORES--> | ||||
| 
 | ||||
|  | @ -98,7 +98,7 @@ http: | |||
| 	debug: | ||||
| 		addr: localhost:5001 | ||||
| notifications: | ||||
| 	endpoints:  | ||||
| 	endpoints: | ||||
| 		- name: alistener | ||||
| 		  disabled: false | ||||
| 		  url: https://my.listener.com/event | ||||
|  | @ -158,7 +158,7 @@ directory. | |||
| >configuration. | ||||
| 
 | ||||
| 
 | ||||
| ## version  | ||||
| ## version | ||||
| 
 | ||||
| ```yaml | ||||
| version: 0.1 | ||||
|  | @ -166,7 +166,7 @@ version: 0.1 | |||
| 
 | ||||
| The `version` option is **required**. It specifies the configuration's version. | ||||
| It is expected to remain a top-level field, to allow for a consistent version | ||||
| check before parsing the remainder of the configuration file.  | ||||
| check before parsing the remainder of the configuration file. | ||||
| 
 | ||||
| ## log | ||||
| 
 | ||||
|  | @ -278,7 +278,7 @@ You must configure one backend; if you configure more, the registry returns an e | |||
| 
 | ||||
| Use the `cache` subsection to enable caching of data accessed in the storage | ||||
| backend. Currently, the only available cache provides fast access to layer | ||||
| metadata. This, if configured, uses the `layerinfo` field.   | ||||
| metadata. This, if configured, uses the `layerinfo` field. | ||||
| 
 | ||||
| You can set `layerinfo` field to `redis` or `inmemory`.  The `redis` value uses | ||||
| a Redis pool to cache layer metadata.  The `inmemory` value uses an in memory | ||||
|  | @ -296,7 +296,7 @@ here so make sure there is adequate space available. | |||
| 
 | ||||
| ### azure | ||||
| 
 | ||||
| This storage backend uses Microsoft's Azure Storage platform.  | ||||
| This storage backend uses Microsoft's Azure Storage platform. | ||||
| 
 | ||||
| <table> | ||||
|   <tr> | ||||
|  | @ -336,7 +336,7 @@ This storage backend uses Microsoft's Azure Storage platform. | |||
|     <td> | ||||
|       Name of the Azure container into which to store data. | ||||
|     </td> | ||||
|   </tr>   | ||||
|   </tr> | ||||
| </table> | ||||
| 
 | ||||
| 
 | ||||
|  | @ -455,7 +455,7 @@ This storage backend uses Amazon's Simple Storage Service (S3). | |||
|     <td> | ||||
|       This is a prefix that will be applied to all S3 keys to allow you to segment data in your bucket if necessary. | ||||
|     </td> | ||||
|   </tr>  | ||||
|   </tr> | ||||
| </table> | ||||
| 
 | ||||
| ### Maintenance | ||||
|  | @ -466,7 +466,7 @@ maintenance functions which are related to storage can be configured under the m | |||
| ### Upload Purging | ||||
| 
 | ||||
| Upload purging is a background process that periodically removes orphaned files from the upload | ||||
| directories of the registry.  Upload purging is enabled by default.  To  | ||||
| directories of the registry.  Upload purging is enabled by default.  To | ||||
|  configure upload directory purging, the following parameters | ||||
| must be set. | ||||
| 
 | ||||
|  | @ -475,10 +475,10 @@ must be set. | |||
|   --------- | -------- | ----------- | ||||
| `enabled` | yes | Set to true to enable upload purging.  Default=true. | | ||||
| `age` | yes | Upload directories which are older than this age will be deleted.  Default=168h (1 week) | ||||
| `interval` | yes | The interval between upload directory purging.  Default=24h.   | ||||
| `interval` | yes | The interval between upload directory purging.  Default=24h. | ||||
| `dryrun` | yes |  dryrun can be set to true to obtain a summary of what directories will be deleted.  Default=false. | ||||
| 
 | ||||
| Note: `age` and `interval` are strings containing a number with optional fraction and a unit suffix: e.g. 45m, 2h10m, 168h (1 week).   | ||||
| Note: `age` and `interval` are strings containing a number with optional fraction and a unit suffix: e.g. 45m, 2h10m, 168h (1 week). | ||||
| 
 | ||||
| ## auth | ||||
| 
 | ||||
|  | @ -505,7 +505,7 @@ The `silly` auth is only for development purposes. It simply checks for the | |||
| existence of the `Authorization` header in the HTTP request. It has no regard for | ||||
| the header's value. If the header does not exist, the `silly` auth responds with a | ||||
| challenge response, echoing back the realm, service, and scope that access was | ||||
| denied for.  | ||||
| denied for. | ||||
| 
 | ||||
| The following values are used to configure the response: | ||||
| 
 | ||||
|  | @ -545,7 +545,7 @@ The following values are used to configure the response: | |||
| 
 | ||||
| Token based authentication allows the authentication system to be decoupled from | ||||
| the registry. It is a well established authentication paradigm with a high | ||||
| degree of security.  | ||||
| degree of security. | ||||
| 
 | ||||
| <table> | ||||
|   <tr> | ||||
|  | @ -592,14 +592,14 @@ the token so it must match the value configured for the issuer. | |||
|       <code>rootcertbundle</code> | ||||
|     </td> | ||||
|     <td> | ||||
| 			yes  | ||||
| 			yes | ||||
|      </td> | ||||
|     <td> | ||||
| The absolute path to the root certificate bundle. This bundle contains the | ||||
| public part of the certificates that is used to sign authentication tokens. | ||||
|      </td> | ||||
|   </tr> | ||||
| </table>  | ||||
| </table> | ||||
| 
 | ||||
| For more information about Token based authentication configuration, see the [specification.] | ||||
| 
 | ||||
|  | @ -613,7 +613,7 @@ object they're wrapping. This means a registry middleware must implement the | |||
| `driver.StorageDriver`. | ||||
| 
 | ||||
| Currently only one middleware, `cloudfront`, a storage middleware, is supported | ||||
| in the registry implementation.  | ||||
| in the registry implementation. | ||||
| 
 | ||||
| ```yaml | ||||
| middleware: | ||||
|  | @ -747,7 +747,7 @@ configuration may contain both. | |||
|       <codde>production</code>,<codde>staging</code>, or | ||||
|       <codde>development</code>. | ||||
|     </td> | ||||
|   </tr>   | ||||
|   </tr> | ||||
|   <tr> | ||||
|     <td> | ||||
|       <code>endpoint</code> | ||||
|  | @ -756,9 +756,9 @@ configuration may contain both. | |||
|       no | ||||
|     </td> | ||||
|     <td> | ||||
|       Specify the enterprise Bugsnag endpoint.  | ||||
|       Specify the enterprise Bugsnag endpoint. | ||||
|     </td> | ||||
|   </tr>   | ||||
|   </tr> | ||||
| </table> | ||||
| 
 | ||||
| 
 | ||||
|  | @ -791,7 +791,7 @@ configuration may contain both. | |||
|     <td> | ||||
|       New Relic application name. | ||||
|     </td> | ||||
|   </tr>  | ||||
|   </tr> | ||||
|      <tr> | ||||
|     <td> | ||||
|       <code>verbose</code> | ||||
|  | @ -802,7 +802,7 @@ configuration may contain both. | |||
|     <td> | ||||
|       Enable New Relic debugging output on stdout. | ||||
|     </td> | ||||
|   </tr>  | ||||
|   </tr> | ||||
| </table> | ||||
| 
 | ||||
| ## http | ||||
|  | @ -810,6 +810,7 @@ configuration may contain both. | |||
| ```yaml | ||||
| http: | ||||
| 	addr: localhost:5000 | ||||
| 	net: tcp | ||||
| 	prefix: /my/nested/registry/ | ||||
| 	secret: asecretforlocaldevelopment | ||||
| 	tls: | ||||
|  | @ -838,7 +839,20 @@ The `http` option details the configuration for the HTTP server that hosts the r | |||
|       yes | ||||
|     </td> | ||||
|     <td> | ||||
|       The <code>HOST:PORT</code> for which the server should accept connections. | ||||
|      The address for which the server should accept connections. The form depends on a network type (see <code>net</code> option): | ||||
|      <code>HOST:PORT</code> for tcp and <code>FILE</code> for a unix socket. | ||||
|     </td> | ||||
|   </tr> | ||||
|   <tr> | ||||
|     <td> | ||||
|       <code>net</code> | ||||
|     </td> | ||||
|     <td> | ||||
|       no | ||||
|     </td> | ||||
|     <td> | ||||
|      The network which is used to create a listening socket. Known networks are <code>unix</code> and <code>tcp</code>. | ||||
|      The default empty value means tcp. | ||||
|     </td> | ||||
|   </tr> | ||||
|     <tr> | ||||
|  | @ -915,7 +929,7 @@ and proxy connections to the registry server. | |||
|     <td> | ||||
|       An array of absolute paths to a x509 CA file | ||||
|     </td> | ||||
|   </tr>   | ||||
|   </tr> | ||||
| </table> | ||||
| 
 | ||||
| 
 | ||||
|  | @ -934,7 +948,7 @@ specifies the `HOST:PORT` on which the debug server should accept connections. | |||
| 
 | ||||
| ```yaml | ||||
| notifications: | ||||
| 	endpoints:  | ||||
| 	endpoints: | ||||
| 		- name: alistener | ||||
| 		  disabled: false | ||||
| 		  url: https://my.listener.com/event | ||||
|  | @ -965,7 +979,7 @@ Endpoints is a list of named services (URLs) that can accept event notifications | |||
|       yes | ||||
|     </td> | ||||
|     <td> | ||||
| A human readable name for the service.      | ||||
| A human readable name for the service. | ||||
| </td> | ||||
|   </tr> | ||||
|   <tr> | ||||
|  | @ -989,7 +1003,7 @@ A boolean to enable/disable notifications for a service. | |||
|     <td> | ||||
| The URL to which events should be published. | ||||
|     </td> | ||||
|   </tr>   | ||||
|   </tr> | ||||
|    <tr> | ||||
|     <td> | ||||
|       <code>headers</code> | ||||
|  | @ -1000,7 +1014,7 @@ The URL to which events should be published. | |||
|     <td> | ||||
|       Static headers to add to each request. | ||||
|     </td> | ||||
|   </tr>  | ||||
|   </tr> | ||||
|   <tr> | ||||
|     <td> | ||||
|       <code>timeout</code> | ||||
|  | @ -1021,7 +1035,7 @@ The URL to which events should be published. | |||
|       </ul> | ||||
|     If you omit the suffix, the system interprets the value as nanoseconds. | ||||
|     </td> | ||||
|   </tr>   | ||||
|   </tr> | ||||
|   <tr> | ||||
|     <td> | ||||
|       <code>threshold</code> | ||||
|  | @ -1032,7 +1046,7 @@ The URL to which events should be published. | |||
|     <td> | ||||
|       An integer specifying how long to wait before backing off a failure. | ||||
|     </td> | ||||
|   </tr>   | ||||
|   </tr> | ||||
|   <tr> | ||||
|     <td> | ||||
|       <code>backoff</code> | ||||
|  | @ -1054,7 +1068,7 @@ The URL to which events should be published. | |||
|       </ul> | ||||
|     If you omit the suffix, the system interprets the value as nanoseconds. | ||||
|     </td> | ||||
|   </tr>   | ||||
|   </tr> | ||||
| </table> | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1129,7 +1143,7 @@ with the [pool](#pool) subsection. | |||
|     <td> | ||||
|       Timeout for connecting to a redis instance. | ||||
|     </td> | ||||
|   </tr>   | ||||
|   </tr> | ||||
|   <tr> | ||||
|     <td> | ||||
|       <code>readtimeout</code> | ||||
|  | @ -1140,7 +1154,7 @@ with the [pool](#pool) subsection. | |||
|     <td> | ||||
|       Timeout for reading from redis connections. | ||||
|     </td> | ||||
|   </tr>    | ||||
|   </tr> | ||||
|   <tr> | ||||
|     <td> | ||||
|       <code>writetimeout</code> | ||||
|  | @ -1151,7 +1165,7 @@ with the [pool](#pool) subsection. | |||
|     <td> | ||||
|       Timeout for writing to redis connections. | ||||
|     </td> | ||||
|   </tr>    | ||||
|   </tr> | ||||
| </table> | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1206,7 +1220,7 @@ Configure the behavior of the Redis connection pool. | |||
|       sets the amount time to wait before closing | ||||
|   inactive connections. | ||||
|     </td> | ||||
|   </tr>   | ||||
|   </tr> | ||||
| </table> | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1216,7 +1230,7 @@ The following is a simple example you can use for local development: | |||
| 
 | ||||
| ```yaml | ||||
| version: 0.1 | ||||
| log:  | ||||
| log: | ||||
| 	level: debug | ||||
| storage: | ||||
|     filesystem: | ||||
|  | @ -1229,7 +1243,7 @@ http: | |||
| ``` | ||||
| 
 | ||||
| The above configures the registry instance to run on port `5000`, binding to | ||||
| `localhost`, with the `debug` server enabled. Registry data storage is in the  | ||||
| `localhost`, with the `debug` server enabled. Registry data storage is in the | ||||
| `/tmp/registry-dev` directory. Logging is in `debug` mode, which is the most | ||||
| verbose. | ||||
| 
 | ||||
|  | @ -1242,7 +1256,7 @@ Both are generally useful for local development. | |||
| 
 | ||||
| This example illustrates how to configure storage middleware in a registry. | ||||
| Middleware allows the registry to serve layers via a content delivery network | ||||
| (CDN). This is useful for reducing requests to the storage layer.   | ||||
| (CDN). This is useful for reducing requests to the storage layer. | ||||
| 
 | ||||
| Currently, the registry supports [Amazon | ||||
| Cloudfront](http://aws.amazon.com/cloudfront/). You can only use Cloudfront in | ||||
|  | @ -1263,7 +1277,7 @@ conjunction with the S3 storage driver. | |||
|   </tr> | ||||
|   <tr> | ||||
|     <td><code>options:</code></td> | ||||
|     <td>  | ||||
|     <td> | ||||
|     A set of key/value options to configure the middleware. | ||||
|     <ul> | ||||
|     <li><code>baseurl:</code> The Cloudfront base URL.</li> | ||||
|  | @ -1293,4 +1307,3 @@ middleware: | |||
| >**Note**: Cloudfront keys exist separately to other AWS keys.  See | ||||
| >[the documentation on AWS credentials](http://docs.aws.amazon.com/AWSSecurityCredentials/1.0/AboutAWSCredentials.html#KeyPairs) | ||||
| >for more information. | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,74 @@ | |||
| package listener | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"os" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
 | ||||
| // connections. It's used by ListenAndServe and ListenAndServeTLS so
 | ||||
| // dead TCP connections (e.g. closing laptop mid-download) eventually
 | ||||
| // go away.
 | ||||
| // it is a plain copy-paste from net/http/server.go
 | ||||
| type tcpKeepAliveListener struct { | ||||
| 	*net.TCPListener | ||||
| } | ||||
| 
 | ||||
| func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { | ||||
| 	tc, err := ln.AcceptTCP() | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	tc.SetKeepAlive(true) | ||||
| 	tc.SetKeepAlivePeriod(3 * time.Minute) | ||||
| 	return tc, nil | ||||
| } | ||||
| 
 | ||||
| // NewListener announces on laddr and net. Accepted values of the net are
 | ||||
| // 'unix' and 'tcp'
 | ||||
| func NewListener(net, laddr string) (net.Listener, error) { | ||||
| 	switch net { | ||||
| 	case "unix": | ||||
| 		return newUnixListener(laddr) | ||||
| 	case "tcp", "": // an empty net means tcp
 | ||||
| 		return newTCPListener(laddr) | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("unknown address type %s", net) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func newUnixListener(laddr string) (net.Listener, error) { | ||||
| 	fi, err := os.Stat(laddr) | ||||
| 	if err == nil { | ||||
| 		// the file exists.
 | ||||
| 		// try to remove it if it's a socket
 | ||||
| 		if !isSocket(fi.Mode()) { | ||||
| 			return nil, fmt.Errorf("file %s exists and is not a socket", laddr) | ||||
| 		} | ||||
| 
 | ||||
| 		if err := os.Remove(laddr); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} else if !os.IsNotExist(err) { | ||||
| 		// we can't do stat on the file.
 | ||||
| 		// it means we can not remove it
 | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return net.Listen("unix", laddr) | ||||
| } | ||||
| 
 | ||||
| func isSocket(m os.FileMode) bool { | ||||
| 	return m&os.ModeSocket != 0 | ||||
| } | ||||
| 
 | ||||
| func newTCPListener(laddr string) (net.Listener, error) { | ||||
| 	ln, err := net.Listen("tcp", laddr) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return tcpKeepAliveListener{ln.(*net.TCPListener)}, nil | ||||
| } | ||||
		Loading…
	
		Reference in New Issue