[Server] Listen and serve on a unix socket

Allow to use a unix socket as a listener.
To specify an endpoint type we use an optional configuration
field 'net', as there's no way to distinguish a relative
socket path from a hostname.

Signed-off-by: Anton Tiurin <noxiouz@yandex.ru>
This commit is contained in:
Anton Tiurin 2015-05-05 11:25:42 +03:00
parent ced8a0378b
commit ad80cbe1ea
5 changed files with 157 additions and 55 deletions

View File

@ -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)
}
}

View File

@ -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.

View File

@ -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 {

View File

@ -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.

View File

@ -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
}