Move initialization code from main.go to the registry package
This makes it easier to embed a registry instance inside another application. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
parent
7a305cc8cd
commit
9b69e40c93
7 changed files with 313 additions and 258 deletions
|
@ -1,28 +1,17 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
_ "expvar"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/Sirupsen/logrus/formatters/logstash"
|
||||
"github.com/bugsnag/bugsnag-go"
|
||||
"github.com/docker/distribution/configuration"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/health"
|
||||
"github.com/docker/distribution/registry"
|
||||
_ "github.com/docker/distribution/registry/auth/htpasswd"
|
||||
_ "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/proxy"
|
||||
_ "github.com/docker/distribution/registry/storage/driver/azure"
|
||||
_ "github.com/docker/distribution/registry/storage/driver/filesystem"
|
||||
|
@ -31,10 +20,8 @@ import (
|
|||
_ "github.com/docker/distribution/registry/storage/driver/oss"
|
||||
_ "github.com/docker/distribution/registry/storage/driver/s3"
|
||||
_ "github.com/docker/distribution/registry/storage/driver/swift"
|
||||
"github.com/docker/distribution/uuid"
|
||||
"github.com/docker/distribution/version"
|
||||
gorhandlers "github.com/gorilla/handlers"
|
||||
"github.com/yvasiyarov/gorelic"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var showVersion bool
|
||||
|
@ -52,99 +39,19 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, "version", version.Version)
|
||||
|
||||
config, err := resolveConfiguration()
|
||||
if err != nil {
|
||||
fatalf("configuration error: %v", err)
|
||||
}
|
||||
|
||||
ctx, err = configureLogging(ctx, config)
|
||||
registry, err := registry.NewRegistry(context.Background(), config)
|
||||
if err != nil {
|
||||
fatalf("error configuring logger: %v", err)
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// inject a logger into the uuid library. warns us if there is a problem
|
||||
// with uuid generation under low entropy.
|
||||
uuid.Loggerf = context.GetLogger(ctx).Warnf
|
||||
|
||||
app := handlers.NewApp(ctx, *config)
|
||||
app.RegisterHealthChecks()
|
||||
handler := configureReporting(app)
|
||||
handler = alive("/", handler)
|
||||
handler = health.Handler(handler)
|
||||
handler = panicHandler(handler)
|
||||
handler = gorhandlers.CombinedLoggingHandler(os.Stdout, handler)
|
||||
|
||||
if config.HTTP.Debug.Addr != "" {
|
||||
go debugServer(config.HTTP.Debug.Addr)
|
||||
}
|
||||
|
||||
server := &http.Server{
|
||||
Handler: handler,
|
||||
}
|
||||
|
||||
ln, err := listener.NewListener(config.HTTP.Net, config.HTTP.Addr)
|
||||
err = registry.Serve()
|
||||
if err != nil {
|
||||
context.GetLogger(app).Fatalln(err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
if config.HTTP.TLS.Certificate != "" {
|
||||
tlsConf := &tls.Config{
|
||||
ClientAuth: tls.NoClientCert,
|
||||
NextProtos: []string{"http/1.1"},
|
||||
Certificates: make([]tls.Certificate, 1),
|
||||
MinVersion: tls.VersionTLS10,
|
||||
PreferServerCipherSuites: true,
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
},
|
||||
}
|
||||
|
||||
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 {
|
||||
pool := x509.NewCertPool()
|
||||
|
||||
for _, ca := range config.HTTP.TLS.ClientCAs {
|
||||
caPem, err := ioutil.ReadFile(ca)
|
||||
if err != nil {
|
||||
context.GetLogger(app).Fatalln(err)
|
||||
}
|
||||
|
||||
if ok := pool.AppendCertsFromPEM(caPem); !ok {
|
||||
context.GetLogger(app).Fatalln(fmt.Errorf("Could not add CA to pool"))
|
||||
}
|
||||
}
|
||||
|
||||
for _, subj := range pool.Subjects() {
|
||||
context.GetLogger(app).Debugf("CA Subject: %s", string(subj))
|
||||
}
|
||||
|
||||
tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
tlsConf.ClientCAs = pool
|
||||
}
|
||||
|
||||
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.Serve(ln); err != nil {
|
||||
context.GetLogger(app).Fatalln(err)
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,148 +93,3 @@ func resolveConfiguration() (*configuration.Configuration, error) {
|
|||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func configureReporting(app *handlers.App) http.Handler {
|
||||
var handler http.Handler = app
|
||||
|
||||
if app.Config.Reporting.Bugsnag.APIKey != "" {
|
||||
bugsnagConfig := bugsnag.Configuration{
|
||||
APIKey: app.Config.Reporting.Bugsnag.APIKey,
|
||||
// TODO(brianbland): provide the registry version here
|
||||
// AppVersion: "2.0",
|
||||
}
|
||||
if app.Config.Reporting.Bugsnag.ReleaseStage != "" {
|
||||
bugsnagConfig.ReleaseStage = app.Config.Reporting.Bugsnag.ReleaseStage
|
||||
}
|
||||
if app.Config.Reporting.Bugsnag.Endpoint != "" {
|
||||
bugsnagConfig.Endpoint = app.Config.Reporting.Bugsnag.Endpoint
|
||||
}
|
||||
bugsnag.Configure(bugsnagConfig)
|
||||
|
||||
handler = bugsnag.Handler(handler)
|
||||
}
|
||||
|
||||
if app.Config.Reporting.NewRelic.LicenseKey != "" {
|
||||
agent := gorelic.NewAgent()
|
||||
agent.NewrelicLicense = app.Config.Reporting.NewRelic.LicenseKey
|
||||
if app.Config.Reporting.NewRelic.Name != "" {
|
||||
agent.NewrelicName = app.Config.Reporting.NewRelic.Name
|
||||
}
|
||||
agent.CollectHTTPStat = true
|
||||
agent.Verbose = app.Config.Reporting.NewRelic.Verbose
|
||||
agent.Run()
|
||||
|
||||
handler = agent.WrapHTTPHandler(handler)
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
// configureLogging prepares the context with a logger using the
|
||||
// configuration.
|
||||
func configureLogging(ctx context.Context, config *configuration.Configuration) (context.Context, error) {
|
||||
if config.Log.Level == "" && config.Log.Formatter == "" {
|
||||
// If no config for logging is set, fallback to deprecated "Loglevel".
|
||||
log.SetLevel(logLevel(config.Loglevel))
|
||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, "version"))
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
log.SetLevel(logLevel(config.Log.Level))
|
||||
|
||||
formatter := config.Log.Formatter
|
||||
if formatter == "" {
|
||||
formatter = "text" // default formatter
|
||||
}
|
||||
|
||||
switch formatter {
|
||||
case "json":
|
||||
log.SetFormatter(&log.JSONFormatter{
|
||||
TimestampFormat: time.RFC3339Nano,
|
||||
})
|
||||
case "text":
|
||||
log.SetFormatter(&log.TextFormatter{
|
||||
TimestampFormat: time.RFC3339Nano,
|
||||
})
|
||||
case "logstash":
|
||||
log.SetFormatter(&logstash.LogstashFormatter{
|
||||
TimestampFormat: time.RFC3339Nano,
|
||||
})
|
||||
default:
|
||||
// just let the library use default on empty string.
|
||||
if config.Log.Formatter != "" {
|
||||
return ctx, fmt.Errorf("unsupported logging formatter: %q", config.Log.Formatter)
|
||||
}
|
||||
}
|
||||
|
||||
if config.Log.Formatter != "" {
|
||||
log.Debugf("using %q logging formatter", config.Log.Formatter)
|
||||
}
|
||||
|
||||
// log the application version with messages
|
||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, "version"))
|
||||
|
||||
if len(config.Log.Fields) > 0 {
|
||||
// build up the static fields, if present.
|
||||
var fields []interface{}
|
||||
for k := range config.Log.Fields {
|
||||
fields = append(fields, k)
|
||||
}
|
||||
|
||||
ctx = context.WithValues(ctx, config.Log.Fields)
|
||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, fields...))
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func logLevel(level configuration.Loglevel) log.Level {
|
||||
l, err := log.ParseLevel(string(level))
|
||||
if err != nil {
|
||||
l = log.InfoLevel
|
||||
log.Warnf("error parsing level %q: %v, using %q ", level, err, l)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
// debugServer starts the debug server with pprof, expvar among other
|
||||
// endpoints. The addr should not be exposed externally. For most of these to
|
||||
// work, tls cannot be enabled on the endpoint, so it is generally separate.
|
||||
func debugServer(addr string) {
|
||||
log.Infof("debug server listening %v", addr)
|
||||
if err := http.ListenAndServe(addr, nil); err != nil {
|
||||
log.Fatalf("error listening on debug interface: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// panicHandler add a HTTP handler to web app. The handler recover the happening
|
||||
// panic. logrus.Panic transmits panic message to pre-config log hooks, which is
|
||||
// defined in config.yml.
|
||||
func panicHandler(handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Panic(fmt.Sprintf("%v", err))
|
||||
}
|
||||
}()
|
||||
handler.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// alive simply wraps the handler with a route that always returns an http 200
|
||||
// response when the path is matched. If the path is not matched, the request
|
||||
// is passed to the provided handler. There is no guarantee of anything but
|
||||
// that the server is up. Wrap with other handlers (such as health.Handler)
|
||||
// for greater affect.
|
||||
func alive(path string, handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == path {
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue