github.com/lusis/distribution@v2.0.1+incompatible/cmd/registry/main.go (about)

     1  package main
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	_ "expvar"
     7  	"flag"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"net/http"
    11  	_ "net/http/pprof"
    12  	"os"
    13  	"time"
    14  
    15  	log "github.com/Sirupsen/logrus"
    16  	"github.com/Sirupsen/logrus/formatters/logstash"
    17  	"github.com/bugsnag/bugsnag-go"
    18  	"github.com/docker/distribution/configuration"
    19  	"github.com/docker/distribution/context"
    20  	_ "github.com/docker/distribution/health"
    21  	_ "github.com/docker/distribution/registry/auth/silly"
    22  	_ "github.com/docker/distribution/registry/auth/token"
    23  	"github.com/docker/distribution/registry/handlers"
    24  	_ "github.com/docker/distribution/registry/storage/driver/azure"
    25  	_ "github.com/docker/distribution/registry/storage/driver/filesystem"
    26  	_ "github.com/docker/distribution/registry/storage/driver/inmemory"
    27  	_ "github.com/docker/distribution/registry/storage/driver/middleware/cloudfront"
    28  	_ "github.com/docker/distribution/registry/storage/driver/s3"
    29  	"github.com/docker/distribution/version"
    30  	gorhandlers "github.com/gorilla/handlers"
    31  	"github.com/yvasiyarov/gorelic"
    32  )
    33  
    34  var showVersion bool
    35  
    36  func init() {
    37  	flag.BoolVar(&showVersion, "version", false, "show the version and exit")
    38  }
    39  
    40  func main() {
    41  	flag.Usage = usage
    42  	flag.Parse()
    43  
    44  	if showVersion {
    45  		version.PrintVersion()
    46  		return
    47  	}
    48  
    49  	ctx := context.Background()
    50  	ctx = context.WithValue(ctx, "version", version.Version)
    51  
    52  	config, err := resolveConfiguration()
    53  	if err != nil {
    54  		fatalf("configuration error: %v", err)
    55  	}
    56  
    57  	ctx, err = configureLogging(ctx, config)
    58  	if err != nil {
    59  		fatalf("error configuring logger: %v", err)
    60  	}
    61  
    62  	app := handlers.NewApp(ctx, *config)
    63  	handler := configureReporting(app)
    64  	handler = gorhandlers.CombinedLoggingHandler(os.Stdout, handler)
    65  
    66  	if config.HTTP.Debug.Addr != "" {
    67  		go debugServer(config.HTTP.Debug.Addr)
    68  	}
    69  
    70  	if config.HTTP.TLS.Certificate == "" {
    71  		context.GetLogger(app).Infof("listening on %v", config.HTTP.Addr)
    72  		if err := http.ListenAndServe(config.HTTP.Addr, handler); err != nil {
    73  			context.GetLogger(app).Fatalln(err)
    74  		}
    75  	} else {
    76  		tlsConf := &tls.Config{
    77  			ClientAuth: tls.NoClientCert,
    78  		}
    79  
    80  		if len(config.HTTP.TLS.ClientCAs) != 0 {
    81  			pool := x509.NewCertPool()
    82  
    83  			for _, ca := range config.HTTP.TLS.ClientCAs {
    84  				caPem, err := ioutil.ReadFile(ca)
    85  				if err != nil {
    86  					context.GetLogger(app).Fatalln(err)
    87  				}
    88  
    89  				if ok := pool.AppendCertsFromPEM(caPem); !ok {
    90  					context.GetLogger(app).Fatalln(fmt.Errorf("Could not add CA to pool"))
    91  				}
    92  			}
    93  
    94  			for _, subj := range pool.Subjects() {
    95  				context.GetLogger(app).Debugf("CA Subject: %s", string(subj))
    96  			}
    97  
    98  			tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
    99  			tlsConf.ClientCAs = pool
   100  		}
   101  
   102  		context.GetLogger(app).Infof("listening on %v, tls", config.HTTP.Addr)
   103  		server := &http.Server{
   104  			Addr:      config.HTTP.Addr,
   105  			Handler:   handler,
   106  			TLSConfig: tlsConf,
   107  		}
   108  
   109  		if err := server.ListenAndServeTLS(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key); err != nil {
   110  			context.GetLogger(app).Fatalln(err)
   111  		}
   112  	}
   113  }
   114  
   115  func usage() {
   116  	fmt.Fprintln(os.Stderr, "usage:", os.Args[0], "<config>")
   117  	flag.PrintDefaults()
   118  }
   119  
   120  func fatalf(format string, args ...interface{}) {
   121  	fmt.Fprintf(os.Stderr, format+"\n", args...)
   122  	usage()
   123  	os.Exit(1)
   124  }
   125  
   126  func resolveConfiguration() (*configuration.Configuration, error) {
   127  	var configurationPath string
   128  
   129  	if flag.NArg() > 0 {
   130  		configurationPath = flag.Arg(0)
   131  	} else if os.Getenv("REGISTRY_CONFIGURATION_PATH") != "" {
   132  		configurationPath = os.Getenv("REGISTRY_CONFIGURATION_PATH")
   133  	}
   134  
   135  	if configurationPath == "" {
   136  		return nil, fmt.Errorf("configuration path unspecified")
   137  	}
   138  
   139  	fp, err := os.Open(configurationPath)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  
   144  	config, err := configuration.Parse(fp)
   145  	if err != nil {
   146  		return nil, fmt.Errorf("error parsing %s: %v", configurationPath, err)
   147  	}
   148  
   149  	return config, nil
   150  }
   151  
   152  func configureReporting(app *handlers.App) http.Handler {
   153  	var handler http.Handler = app
   154  
   155  	if app.Config.Reporting.Bugsnag.APIKey != "" {
   156  		bugsnagConfig := bugsnag.Configuration{
   157  			APIKey: app.Config.Reporting.Bugsnag.APIKey,
   158  			// TODO(brianbland): provide the registry version here
   159  			// AppVersion: "2.0",
   160  		}
   161  		if app.Config.Reporting.Bugsnag.ReleaseStage != "" {
   162  			bugsnagConfig.ReleaseStage = app.Config.Reporting.Bugsnag.ReleaseStage
   163  		}
   164  		if app.Config.Reporting.Bugsnag.Endpoint != "" {
   165  			bugsnagConfig.Endpoint = app.Config.Reporting.Bugsnag.Endpoint
   166  		}
   167  		bugsnag.Configure(bugsnagConfig)
   168  
   169  		handler = bugsnag.Handler(handler)
   170  	}
   171  
   172  	if app.Config.Reporting.NewRelic.LicenseKey != "" {
   173  		agent := gorelic.NewAgent()
   174  		agent.NewrelicLicense = app.Config.Reporting.NewRelic.LicenseKey
   175  		if app.Config.Reporting.NewRelic.Name != "" {
   176  			agent.NewrelicName = app.Config.Reporting.NewRelic.Name
   177  		}
   178  		agent.CollectHTTPStat = true
   179  		agent.Verbose = app.Config.Reporting.NewRelic.Verbose
   180  		agent.Run()
   181  
   182  		handler = agent.WrapHTTPHandler(handler)
   183  	}
   184  
   185  	return handler
   186  }
   187  
   188  // configureLogging prepares the context with a logger using the
   189  // configuration.
   190  func configureLogging(ctx context.Context, config *configuration.Configuration) (context.Context, error) {
   191  	if config.Log.Level == "" && config.Log.Formatter == "" {
   192  		// If no config for logging is set, fallback to deprecated "Loglevel".
   193  		log.SetLevel(logLevel(config.Loglevel))
   194  		ctx = context.WithLogger(ctx, context.GetLogger(ctx, "version"))
   195  		return ctx, nil
   196  	}
   197  
   198  	log.SetLevel(logLevel(config.Log.Level))
   199  
   200  	formatter := config.Log.Formatter
   201  	if formatter == "" {
   202  		formatter = "text" // default formatter
   203  	}
   204  
   205  	switch formatter {
   206  	case "json":
   207  		log.SetFormatter(&log.JSONFormatter{
   208  			TimestampFormat: time.RFC3339Nano,
   209  		})
   210  	case "text":
   211  		log.SetFormatter(&log.TextFormatter{
   212  			TimestampFormat: time.RFC3339Nano,
   213  		})
   214  	case "logstash":
   215  		log.SetFormatter(&logstash.LogstashFormatter{
   216  			TimestampFormat: time.RFC3339Nano,
   217  		})
   218  	default:
   219  		// just let the library use default on empty string.
   220  		if config.Log.Formatter != "" {
   221  			return ctx, fmt.Errorf("unsupported logging formatter: %q", config.Log.Formatter)
   222  		}
   223  	}
   224  
   225  	if config.Log.Formatter != "" {
   226  		log.Debugf("using %q logging formatter", config.Log.Formatter)
   227  	}
   228  
   229  	// log the application version with messages
   230  	ctx = context.WithLogger(ctx, context.GetLogger(ctx, "version"))
   231  
   232  	if len(config.Log.Fields) > 0 {
   233  		// build up the static fields, if present.
   234  		var fields []interface{}
   235  		for k := range config.Log.Fields {
   236  			fields = append(fields, k)
   237  		}
   238  
   239  		ctx = context.WithValues(ctx, config.Log.Fields)
   240  		ctx = context.WithLogger(ctx, context.GetLogger(ctx, fields...))
   241  	}
   242  
   243  	return ctx, nil
   244  }
   245  
   246  func logLevel(level configuration.Loglevel) log.Level {
   247  	l, err := log.ParseLevel(string(level))
   248  	if err != nil {
   249  		l = log.InfoLevel
   250  		log.Warnf("error parsing level %q: %v, using %q	", level, err, l)
   251  	}
   252  
   253  	return l
   254  }
   255  
   256  // debugServer starts the debug server with pprof, expvar among other
   257  // endpoints. The addr should not be exposed externally. For most of these to
   258  // work, tls cannot be enabled on the endpoint, so it is generally separate.
   259  func debugServer(addr string) {
   260  	log.Infof("debug server listening %v", addr)
   261  	if err := http.ListenAndServe(addr, nil); err != nil {
   262  		log.Fatalf("error listening on debug interface: %v", err)
   263  	}
   264  }