<commit_before>package registry

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/distribution/registry/api/errcode"
	"github.com/docker/distribution/registry/api/v2"
	"github.com/docker/distribution/registry/client/transport"
	"github.com/docker/docker/autogen/dockerversion"
	"github.com/docker/docker/pkg/parsers/kernel"
	"github.com/docker/docker/pkg/tlsconfig"
	"github.com/docker/docker/pkg/useragent"
)

var (
	// ErrAlreadyExists is an error returned if an image being pushed
	// already exists on the remote side
	ErrAlreadyExists = errors.New("Image already exists")
	errLoginRequired = errors.New("Authentication is required.")
)

// dockerUserAgent is the User-Agent the Docker client uses to identify itself.
// It is populated on init(), comprising version information of different components.
var dockerUserAgent string

func init() {
	httpVersion := make([]useragent.VersionInfo, 0, 6)
	httpVersion = append(httpVersion, useragent.VersionInfo{"docker", dockerversion.VERSION})
	httpVersion = append(httpVersion, useragent.VersionInfo{"go", runtime.Version()})
	httpVersion = append(httpVersion, useragent.VersionInfo{"git-commit", dockerversion.GITCOMMIT})
	if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
		httpVersion = append(httpVersion, useragent.VersionInfo{"kernel", kernelVersion.String()})
	}
	httpVersion = append(httpVersion, useragent.VersionInfo{"os", runtime.GOOS})
	httpVersion = append(httpVersion, useragent.VersionInfo{"arch", runtime.GOARCH})

	dockerUserAgent = useragent.AppendVersions("", httpVersion...)
}

func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) {
	// PreferredServerCipherSuites should have no effect
	tlsConfig := tlsconfig.ServerDefault

	tlsConfig.InsecureSkipVerify = !isSecure

	if isSecure {
		hostDir := filepath.Join(CertsDir, hostname)
		logrus.Debugf("hostDir: %s", hostDir)
		if err := ReadCertsDirectory(&tlsConfig, hostDir); err != nil {
			return nil, err
		}
	}

	return &tlsConfig, nil
}

func hasFile(files []os.FileInfo, name string) bool {
	for _, f := range files {
		if f.Name() == name {
			return true
		}
	}
	return false
}

// ReadCertsDirectory reads the directory for TLS certificates
// including roots and certificate pairs and updates the
// provided TLS configuration.
func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
	fs, err := ioutil.ReadDir(directory)
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	for _, f := range fs {
		if strings.HasSuffix(f.Name(), ".crt") {
			if tlsConfig.RootCAs == nil {
				// TODO(dmcgowan): Copy system pool
				tlsConfig.RootCAs = x509.NewCertPool()
			}
			logrus.Debugf("crt: %s", filepath.Join(directory, f.Name()))
			data, err := ioutil.ReadFile(filepath.Join(directory, f.Name()))
			if err != nil {
				return err
			}
			tlsConfig.RootCAs.AppendCertsFromPEM(data)
		}
		if strings.HasSuffix(f.Name(), ".cert") {
			certName := f.Name()
			keyName := certName[:len(certName)-5] + ".key"
			logrus.Debugf("cert: %s", filepath.Join(directory, f.Name()))
			if !hasFile(fs, keyName) {
				return fmt.Errorf("Missing key %s for certificate %s", keyName, certName)
			}
			cert, err := tls.LoadX509KeyPair(filepath.Join(directory, certName), filepath.Join(directory, keyName))
			if err != nil {
				return err
			}
			tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
		}
		if strings.HasSuffix(f.Name(), ".key") {
			keyName := f.Name()
			certName := keyName[:len(keyName)-4] + ".cert"
			logrus.Debugf("key: %s", filepath.Join(directory, f.Name()))
			if !hasFile(fs, certName) {
				return fmt.Errorf("Missing certificate %s for key %s", certName, keyName)
			}
		}
	}

	return nil
}

// DockerHeaders returns request modifiers that ensure requests have
// the User-Agent header set to dockerUserAgent and that metaHeaders
// are added.
func DockerHeaders(metaHeaders http.Header) []transport.RequestModifier {
	modifiers := []transport.RequestModifier{
		transport.NewHeaderRequestModifier(http.Header{"User-Agent": []string{dockerUserAgent}}),
	}
	if metaHeaders != nil {
		modifiers = append(modifiers, transport.NewHeaderRequestModifier(metaHeaders))
	}
	return modifiers
}

// HTTPClient returns a HTTP client structure which uses the given transport
// and contains the necessary headers for redirected requests
func HTTPClient(transport http.RoundTripper) *http.Client {
	return &http.Client{
		Transport:     transport,
		CheckRedirect: addRequiredHeadersToRedirectedRequests,
	}
}

func trustedLocation(req *http.Request) bool {
	var (
		trusteds = []string{"docker.com", "docker.io"}
		hostname = strings.SplitN(req.Host, ":", 2)[0]
	)
	if req.URL.Scheme != "https" {
		return false
	}

	for _, trusted := range trusteds {
		if hostname == trusted || strings.HasSuffix(hostname, "."+trusted) {
			return true
		}
	}
	return false
}

// addRequiredHeadersToRedirectedRequests adds the necessary redirection headers
// for redirected requests
func addRequiredHeadersToRedirectedRequests(req *http.Request, via []*http.Request) error {
	if via != nil && via[0] != nil {
		if trustedLocation(req) && trustedLocation(via[0]) {
			req.Header = via[0].Header
			return nil
		}
		for k, v := range via[0].Header {
			if k != "Authorization" {
				for _, vv := range v {
					req.Header.Add(k, vv)
				}
			}
		}
	}
	return nil
}

func shouldV2Fallback(err errcode.Error) bool {
	logrus.Debugf("v2 error: %T %v", err, err)
	switch err.Code {
	case v2.ErrorCodeUnauthorized, v2.ErrorCodeManifestUnknown:
		return true
	}
	return false
}

// ErrNoSupport is an error type used for errors indicating that an operation
// is not supported. It encapsulates a more specific error.
type ErrNoSupport struct{ Err error }

func (e ErrNoSupport) Error() string {
	if e.Err == nil {
		return "not supported"
	}
	return e.Err.Error()
}

// ContinueOnError returns true if we should fallback to the next endpoint
// as a result of this error.
func ContinueOnError(err error) bool {
	switch v := err.(type) {
	case errcode.Errors:
		return ContinueOnError(v[0])
	case ErrNoSupport:
		return ContinueOnError(v.Err)
	case errcode.Error:
		return shouldV2Fallback(v)
	}
	return false
}

// NewTransport returns a new HTTP transport. If tlsConfig is nil, it uses the
// default TLS configuration.
func NewTransport(tlsConfig *tls.Config) *http.Transport {
	if tlsConfig == nil {
		var cfg = tlsconfig.ServerDefault
		tlsConfig = &cfg
	}
	return &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
			DualStack: true,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig:     tlsConfig,
		// TODO(dmcgowan): Call close idle connections when complete and use keep alive
		DisableKeepAlives: true,
	}
}
<commit_msg>registry: allow fallback on unknown errors<commit_after>package registry

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/distribution/registry/api/errcode"
	"github.com/docker/distribution/registry/api/v2"
	"github.com/docker/distribution/registry/client"
	"github.com/docker/distribution/registry/client/transport"
	"github.com/docker/docker/autogen/dockerversion"
	"github.com/docker/docker/pkg/parsers/kernel"
	"github.com/docker/docker/pkg/tlsconfig"
	"github.com/docker/docker/pkg/useragent"
)

var (
	// ErrAlreadyExists is an error returned if an image being pushed
	// already exists on the remote side
	ErrAlreadyExists = errors.New("Image already exists")
	errLoginRequired = errors.New("Authentication is required.")
)

// dockerUserAgent is the User-Agent the Docker client uses to identify itself.
// It is populated on init(), comprising version information of different components.
var dockerUserAgent string

func init() {
	httpVersion := make([]useragent.VersionInfo, 0, 6)
	httpVersion = append(httpVersion, useragent.VersionInfo{"docker", dockerversion.VERSION})
	httpVersion = append(httpVersion, useragent.VersionInfo{"go", runtime.Version()})
	httpVersion = append(httpVersion, useragent.VersionInfo{"git-commit", dockerversion.GITCOMMIT})
	if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
		httpVersion = append(httpVersion, useragent.VersionInfo{"kernel", kernelVersion.String()})
	}
	httpVersion = append(httpVersion, useragent.VersionInfo{"os", runtime.GOOS})
	httpVersion = append(httpVersion, useragent.VersionInfo{"arch", runtime.GOARCH})

	dockerUserAgent = useragent.AppendVersions("", httpVersion...)
}

func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) {
	// PreferredServerCipherSuites should have no effect
	tlsConfig := tlsconfig.ServerDefault

	tlsConfig.InsecureSkipVerify = !isSecure

	if isSecure {
		hostDir := filepath.Join(CertsDir, hostname)
		logrus.Debugf("hostDir: %s", hostDir)
		if err := ReadCertsDirectory(&tlsConfig, hostDir); err != nil {
			return nil, err
		}
	}

	return &tlsConfig, nil
}

func hasFile(files []os.FileInfo, name string) bool {
	for _, f := range files {
		if f.Name() == name {
			return true
		}
	}
	return false
}

// ReadCertsDirectory reads the directory for TLS certificates
// including roots and certificate pairs and updates the
// provided TLS configuration.
func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
	fs, err := ioutil.ReadDir(directory)
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	for _, f := range fs {
		if strings.HasSuffix(f.Name(), ".crt") {
			if tlsConfig.RootCAs == nil {
				// TODO(dmcgowan): Copy system pool
				tlsConfig.RootCAs = x509.NewCertPool()
			}
			logrus.Debugf("crt: %s", filepath.Join(directory, f.Name()))
			data, err := ioutil.ReadFile(filepath.Join(directory, f.Name()))
			if err != nil {
				return err
			}
			tlsConfig.RootCAs.AppendCertsFromPEM(data)
		}
		if strings.HasSuffix(f.Name(), ".cert") {
			certName := f.Name()
			keyName := certName[:len(certName)-5] + ".key"
			logrus.Debugf("cert: %s", filepath.Join(directory, f.Name()))
			if !hasFile(fs, keyName) {
				return fmt.Errorf("Missing key %s for certificate %s", keyName, certName)
			}
			cert, err := tls.LoadX509KeyPair(filepath.Join(directory, certName), filepath.Join(directory, keyName))
			if err != nil {
				return err
			}
			tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
		}
		if strings.HasSuffix(f.Name(), ".key") {
			keyName := f.Name()
			certName := keyName[:len(keyName)-4] + ".cert"
			logrus.Debugf("key: %s", filepath.Join(directory, f.Name()))
			if !hasFile(fs, certName) {
				return fmt.Errorf("Missing certificate %s for key %s", certName, keyName)
			}
		}
	}

	return nil
}

// DockerHeaders returns request modifiers that ensure requests have
// the User-Agent header set to dockerUserAgent and that metaHeaders
// are added.
func DockerHeaders(metaHeaders http.Header) []transport.RequestModifier {
	modifiers := []transport.RequestModifier{
		transport.NewHeaderRequestModifier(http.Header{"User-Agent": []string{dockerUserAgent}}),
	}
	if metaHeaders != nil {
		modifiers = append(modifiers, transport.NewHeaderRequestModifier(metaHeaders))
	}
	return modifiers
}

// HTTPClient returns a HTTP client structure which uses the given transport
// and contains the necessary headers for redirected requests
func HTTPClient(transport http.RoundTripper) *http.Client {
	return &http.Client{
		Transport:     transport,
		CheckRedirect: addRequiredHeadersToRedirectedRequests,
	}
}

func trustedLocation(req *http.Request) bool {
	var (
		trusteds = []string{"docker.com", "docker.io"}
		hostname = strings.SplitN(req.Host, ":", 2)[0]
	)
	if req.URL.Scheme != "https" {
		return false
	}

	for _, trusted := range trusteds {
		if hostname == trusted || strings.HasSuffix(hostname, "."+trusted) {
			return true
		}
	}
	return false
}

// addRequiredHeadersToRedirectedRequests adds the necessary redirection headers
// for redirected requests
func addRequiredHeadersToRedirectedRequests(req *http.Request, via []*http.Request) error {
	if via != nil && via[0] != nil {
		if trustedLocation(req) && trustedLocation(via[0]) {
			req.Header = via[0].Header
			return nil
		}
		for k, v := range via[0].Header {
			if k != "Authorization" {
				for _, vv := range v {
					req.Header.Add(k, vv)
				}
			}
		}
	}
	return nil
}

func shouldV2Fallback(err errcode.Error) bool {
	logrus.Debugf("v2 error: %T %v", err, err)
	switch err.Code {
	case v2.ErrorCodeUnauthorized, v2.ErrorCodeManifestUnknown:
		return true
	}
	return false
}

// ErrNoSupport is an error type used for errors indicating that an operation
// is not supported. It encapsulates a more specific error.
type ErrNoSupport struct{ Err error }

func (e ErrNoSupport) Error() string {
	if e.Err == nil {
		return "not supported"
	}
	return e.Err.Error()
}

// ContinueOnError returns true if we should fallback to the next endpoint
// as a result of this error.
func ContinueOnError(err error) bool {
	switch v := err.(type) {
	case errcode.Errors:
		return ContinueOnError(v[0])
	case ErrNoSupport:
		return ContinueOnError(v.Err)
	case errcode.Error:
		return shouldV2Fallback(v)
	case *client.UnexpectedHTTPResponseError:
		return true
	}
	// let's be nice and fallback if the error is a completely
	// unexpected one.
	// If new errors have to be handled in some way, please
	// add them to the switch above.
	return true
}

// NewTransport returns a new HTTP transport. If tlsConfig is nil, it uses the
// default TLS configuration.
func NewTransport(tlsConfig *tls.Config) *http.Transport {
	if tlsConfig == nil {
		var cfg = tlsconfig.ServerDefault
		tlsConfig = &cfg
	}
	return &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
			DualStack: true,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig:     tlsConfig,
		// TODO(dmcgowan): Call close idle connections when complete and use keep alive
		DisableKeepAlives: true,
	}
}
<|endoftext|>


<commit_before>package registry

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"rsc.io/letsencrypt"

	logstash "github.com/bshuster-repo/logrus-logstash-hook"
	"github.com/bugsnag/bugsnag-go"
	"github.com/docker/distribution/configuration"
	dcontext "github.com/docker/distribution/context"
	"github.com/docker/distribution/health"
	"github.com/docker/distribution/registry/handlers"
	"github.com/docker/distribution/registry/listener"
	"github.com/docker/distribution/uuid"
	"github.com/docker/distribution/version"
	"github.com/docker/go-metrics"
	gorhandlers "github.com/gorilla/handlers"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/yvasiyarov/gorelic"
)

// ServeCmd is a cobra command for running the registry.
var ServeCmd = &cobra.Command{
	Use:   "serve <config>",
	Short: "`serve` stores and distributes Docker images",
	Long:  "`serve` stores and distributes Docker images.",
	Run: func(cmd *cobra.Command, args []string) {

		// setup context
		ctx := dcontext.WithVersion(dcontext.Background(), version.Version)

		config, err := resolveConfiguration(args)
		if err != nil {
			fmt.Fprintf(os.Stderr, "configuration error: %v\n", err)
			cmd.Usage()
			os.Exit(1)
		}

		if config.HTTP.Debug.Addr != "" {
			go func(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)
				}
			}(config.HTTP.Debug.Addr)
		}

		registry, err := NewRegistry(ctx, config)
		if err != nil {
			log.Fatalln(err)
		}

		if config.HTTP.Debug.Prometheus.Enabled {
			path := config.HTTP.Debug.Prometheus.Path
			if path == "" {
				path = "/metrics"
			}
			log.Info("providing prometheus metrics on ", path)
			http.Handle(path, metrics.Handler())
		}

		if err = registry.ListenAndServe(); err != nil {
			log.Fatalln(err)
		}
	},
}

// A Registry represents a complete instance of the registry.
// TODO(aaronl): It might make sense for Registry to become an interface.
type Registry struct {
	config *configuration.Configuration
	app    *handlers.App
	server *http.Server
}

// NewRegistry creates a new registry from a context and configuration struct.
func NewRegistry(ctx context.Context, config *configuration.Configuration) (*Registry, error) {
	var err error
	ctx, err = configureLogging(ctx, config)
	if err != nil {
		return nil, fmt.Errorf("error configuring logger: %v", err)
	}

	// inject a logger into the uuid library. warns us if there is a problem
	// with uuid generation under low entropy.
	uuid.Loggerf = dcontext.GetLogger(ctx).Warnf

	app := handlers.NewApp(ctx, config)
	// TODO(aaronl): The global scope of the health checks means NewRegistry
	// can only be called once per process.
	app.RegisterHealthChecks()
	handler := configureReporting(app)
	handler = alive("/", handler)
	handler = health.Handler(handler)
	handler = panicHandler(handler)
	if !config.Log.AccessLog.Disabled {
		handler = gorhandlers.CombinedLoggingHandler(os.Stdout, handler)
	}

	server := &http.Server{
		Handler: handler,
	}

	return &Registry{
		app:    app,
		config: config,
		server: server,
	}, nil
}

// ListenAndServe runs the registry's HTTP server.
func (registry *Registry) ListenAndServe() error {
	config := registry.config

	ln, err := listener.NewListener(config.HTTP.Net, config.HTTP.Addr)
	if err != nil {
		return err
	}

	if config.HTTP.TLS.Certificate != "" || config.HTTP.TLS.LetsEncrypt.CacheFile != "" {
		tlsConf := &tls.Config{
			ClientAuth:               tls.NoClientCert,
			NextProtos:               nextProtos(config),
			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,
			},
		}

		if config.HTTP.TLS.LetsEncrypt.CacheFile != "" {
			if config.HTTP.TLS.Certificate != "" {
				return fmt.Errorf("cannot specify both certificate and Let's Encrypt")
			}
			var m letsencrypt.Manager
			if err := m.CacheFile(config.HTTP.TLS.LetsEncrypt.CacheFile); err != nil {
				return err
			}
			if !m.Registered() {
				if err := m.Register(config.HTTP.TLS.LetsEncrypt.Email, nil); err != nil {
					return err
				}
			}
			if len(config.HTTP.TLS.LetsEncrypt.Hosts) > 0 {
				m.SetHosts(config.HTTP.TLS.LetsEncrypt.Hosts)
			}
			tlsConf.GetCertificate = m.GetCertificate
		} else {
			tlsConf.Certificates = make([]tls.Certificate, 1)
			tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key)
			if err != nil {
				return 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 {
					return err
				}

				if ok := pool.AppendCertsFromPEM(caPem); !ok {
					return fmt.Errorf("Could not add CA to pool")
				}
			}

			for _, subj := range pool.Subjects() {
				dcontext.GetLogger(registry.app).Debugf("CA Subject: %s", string(subj))
			}

			tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
			tlsConf.ClientCAs = pool
		}

		ln = tls.NewListener(ln, tlsConf)
		dcontext.GetLogger(registry.app).Infof("listening on %v, tls", ln.Addr())
	} else {
		dcontext.GetLogger(registry.app).Infof("listening on %v", ln.Addr())
	}

	return registry.server.Serve(ln)
}

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 = dcontext.WithLogger(ctx, dcontext.GetLogger(ctx))
		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)
	}

	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 = dcontext.WithValues(ctx, config.Log.Fields)
		ctx = dcontext.WithLogger(ctx, dcontext.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
}

// panicHandler add an 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)
	})
}

func resolveConfiguration(args []string) (*configuration.Configuration, error) {
	var configurationPath string

	if len(args) > 0 {
		configurationPath = args[0]
	} else if os.Getenv("REGISTRY_CONFIGURATION_PATH") != "" {
		configurationPath = os.Getenv("REGISTRY_CONFIGURATION_PATH")
	}

	if configurationPath == "" {
		return nil, fmt.Errorf("configuration path unspecified")
	}

	fp, err := os.Open(configurationPath)
	if err != nil {
		return nil, err
	}

	defer fp.Close()

	config, err := configuration.Parse(fp)
	if err != nil {
		return nil, fmt.Errorf("error parsing %s: %v", configurationPath, err)
	}

	return config, nil
}

func nextProtos(config *configuration.Configuration) []string {
	switch config.HTTP.HTTP2.Disabled {
	case true:
		return []string{"http/1.1"}
	default:
		return []string{"h2", "http/1.1"}
	}
}
<commit_msg>Remove ciphers that do not support perfect forward secrecy<commit_after>package registry

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"rsc.io/letsencrypt"

	logstash "github.com/bshuster-repo/logrus-logstash-hook"
	"github.com/bugsnag/bugsnag-go"
	"github.com/docker/distribution/configuration"
	dcontext "github.com/docker/distribution/context"
	"github.com/docker/distribution/health"
	"github.com/docker/distribution/registry/handlers"
	"github.com/docker/distribution/registry/listener"
	"github.com/docker/distribution/uuid"
	"github.com/docker/distribution/version"
	"github.com/docker/go-metrics"
	gorhandlers "github.com/gorilla/handlers"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/yvasiyarov/gorelic"
)

// ServeCmd is a cobra command for running the registry.
var ServeCmd = &cobra.Command{
	Use:   "serve <config>",
	Short: "`serve` stores and distributes Docker images",
	Long:  "`serve` stores and distributes Docker images.",
	Run: func(cmd *cobra.Command, args []string) {

		// setup context
		ctx := dcontext.WithVersion(dcontext.Background(), version.Version)

		config, err := resolveConfiguration(args)
		if err != nil {
			fmt.Fprintf(os.Stderr, "configuration error: %v\n", err)
			cmd.Usage()
			os.Exit(1)
		}

		if config.HTTP.Debug.Addr != "" {
			go func(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)
				}
			}(config.HTTP.Debug.Addr)
		}

		registry, err := NewRegistry(ctx, config)
		if err != nil {
			log.Fatalln(err)
		}

		if config.HTTP.Debug.Prometheus.Enabled {
			path := config.HTTP.Debug.Prometheus.Path
			if path == "" {
				path = "/metrics"
			}
			log.Info("providing prometheus metrics on ", path)
			http.Handle(path, metrics.Handler())
		}

		if err = registry.ListenAndServe(); err != nil {
			log.Fatalln(err)
		}
	},
}

// A Registry represents a complete instance of the registry.
// TODO(aaronl): It might make sense for Registry to become an interface.
type Registry struct {
	config *configuration.Configuration
	app    *handlers.App
	server *http.Server
}

// NewRegistry creates a new registry from a context and configuration struct.
func NewRegistry(ctx context.Context, config *configuration.Configuration) (*Registry, error) {
	var err error
	ctx, err = configureLogging(ctx, config)
	if err != nil {
		return nil, fmt.Errorf("error configuring logger: %v", err)
	}

	// inject a logger into the uuid library. warns us if there is a problem
	// with uuid generation under low entropy.
	uuid.Loggerf = dcontext.GetLogger(ctx).Warnf

	app := handlers.NewApp(ctx, config)
	// TODO(aaronl): The global scope of the health checks means NewRegistry
	// can only be called once per process.
	app.RegisterHealthChecks()
	handler := configureReporting(app)
	handler = alive("/", handler)
	handler = health.Handler(handler)
	handler = panicHandler(handler)
	if !config.Log.AccessLog.Disabled {
		handler = gorhandlers.CombinedLoggingHandler(os.Stdout, handler)
	}

	server := &http.Server{
		Handler: handler,
	}

	return &Registry{
		app:    app,
		config: config,
		server: server,
	}, nil
}

// ListenAndServe runs the registry's HTTP server.
func (registry *Registry) ListenAndServe() error {
	config := registry.config

	ln, err := listener.NewListener(config.HTTP.Net, config.HTTP.Addr)
	if err != nil {
		return err
	}

	if config.HTTP.TLS.Certificate != "" || config.HTTP.TLS.LetsEncrypt.CacheFile != "" {
		tlsConf := &tls.Config{
			ClientAuth:               tls.NoClientCert,
			NextProtos:               nextProtos(config),
			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,
			},
		}

		if config.HTTP.TLS.LetsEncrypt.CacheFile != "" {
			if config.HTTP.TLS.Certificate != "" {
				return fmt.Errorf("cannot specify both certificate and Let's Encrypt")
			}
			var m letsencrypt.Manager
			if err := m.CacheFile(config.HTTP.TLS.LetsEncrypt.CacheFile); err != nil {
				return err
			}
			if !m.Registered() {
				if err := m.Register(config.HTTP.TLS.LetsEncrypt.Email, nil); err != nil {
					return err
				}
			}
			if len(config.HTTP.TLS.LetsEncrypt.Hosts) > 0 {
				m.SetHosts(config.HTTP.TLS.LetsEncrypt.Hosts)
			}
			tlsConf.GetCertificate = m.GetCertificate
		} else {
			tlsConf.Certificates = make([]tls.Certificate, 1)
			tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key)
			if err != nil {
				return 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 {
					return err
				}

				if ok := pool.AppendCertsFromPEM(caPem); !ok {
					return fmt.Errorf("Could not add CA to pool")
				}
			}

			for _, subj := range pool.Subjects() {
				dcontext.GetLogger(registry.app).Debugf("CA Subject: %s", string(subj))
			}

			tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
			tlsConf.ClientCAs = pool
		}

		ln = tls.NewListener(ln, tlsConf)
		dcontext.GetLogger(registry.app).Infof("listening on %v, tls", ln.Addr())
	} else {
		dcontext.GetLogger(registry.app).Infof("listening on %v", ln.Addr())
	}

	return registry.server.Serve(ln)
}

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 = dcontext.WithLogger(ctx, dcontext.GetLogger(ctx))
		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)
	}

	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 = dcontext.WithValues(ctx, config.Log.Fields)
		ctx = dcontext.WithLogger(ctx, dcontext.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
}

// panicHandler add an 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)
	})
}

func resolveConfiguration(args []string) (*configuration.Configuration, error) {
	var configurationPath string

	if len(args) > 0 {
		configurationPath = args[0]
	} else if os.Getenv("REGISTRY_CONFIGURATION_PATH") != "" {
		configurationPath = os.Getenv("REGISTRY_CONFIGURATION_PATH")
	}

	if configurationPath == "" {
		return nil, fmt.Errorf("configuration path unspecified")
	}

	fp, err := os.Open(configurationPath)
	if err != nil {
		return nil, err
	}

	defer fp.Close()

	config, err := configuration.Parse(fp)
	if err != nil {
		return nil, fmt.Errorf("error parsing %s: %v", configurationPath, err)
	}

	return config, nil
}

func nextProtos(config *configuration.Configuration) []string {
	switch config.HTTP.HTTP2.Disabled {
	case true:
		return []string{"http/1.1"}
	default:
		return []string{"h2", "http/1.1"}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Helper code for daemonizing gcsfuse, synchronizing on successful mount.
//
// The details of this package are subject to change.
package daemon

import (
	"errors"
	"io"
)

// The name of an environment variable used to communicate a file descriptor
// set up by Mount to the gcsfuse subprocess. Gob encoding is used to
// communicate back to Mount.
const envVar = "MOUNT_STATUS_FD"

// A message containing logging output for the process of mounting the file
// system.
type logMsg struct {
	Msg []byte
}

// A message indicating the outcome of the process of mounting the file system.
// The receiver ignores further messages.
type outcomeMsg struct {
	Succesful bool

	// Meaningful only if !Succesful.
	ErrorMsg string
}

// For use by gcsfuse: signal that mounting was successful (allowing the caller
// of the process to return in success) or that there was a failure to mount
// the file system (allowing the caller of the process to display an
// appropriate error message).
//
// Do nothing if the process wasn't invoked with Mount.
func SignalOutcome(outcome error) (err error) {
	err = errors.New("TODO")
	return
}

// For use by gcsfuse: return a writer that should be used for logging status
// messages while in the process of mounting.
//
// The returned writer must not be written to after calling SignalOutcome.
func StatusWriter() (w io.Writer) {
	panic("TODO")
}

// Invoke gcsfuse with the supplied arguments, waiting until it successfully
// mounts or reports that is has failed. Write status updates while mounting
// into the supplied writer (which may be nil for silence). Return nil only if
// it mounts successfully.
func Mount(
	gcsfusePath string,
	args []string,
	status io.Writer) (err error) {
	err = errors.New("TODO")
	return
}
<commit_msg>Initialize the file at startup.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Helper code for daemonizing gcsfuse, synchronizing on successful mount.
//
// The details of this package are subject to change.
package daemon

import (
	"errors"
	"io"
	"log"
	"os"
	"strconv"
)

// The name of an environment variable used to communicate a file descriptor
// set up by Mount to the gcsfuse subprocess. Gob encoding is used to
// communicate back to Mount.
const envVar = "MOUNT_STATUS_FD"

// A message containing logging output for the process of mounting the file
// system.
type logMsg struct {
	Msg []byte
}

// A message indicating the outcome of the process of mounting the file system.
// The receiver ignores further messages.
type outcomeMsg struct {
	Succesful bool

	// Meaningful only if !Succesful.
	ErrorMsg string
}

// The file provded to this process via the environment variable, or nil if
// none.
var gFile *os.File

func init() {
	// Is the environment variable set?
	fdStr, ok := os.LookupEnv(envVar)
	if !ok {
		return
	}

	// Parse the file descriptor.
	fd, err := strconv.ParseUint(fdStr, 10, 32)
	if err != nil {
		log.Fatalf("Couldn't parse %s value %q: %v", envVar, fdStr, err)
	}

	gFile = os.NewFile(uintptr(fd), envVar)
}

// For use by gcsfuse: signal that mounting was successful (allowing the caller
// of the process to return in success) or that there was a failure to mount
// the file system (allowing the caller of the process to display an
// appropriate error message).
//
// Do nothing if the process wasn't invoked with Mount.
func SignalOutcome(outcome error) (err error) {
	err = errors.New("TODO")
	return
}

// For use by gcsfuse: return a writer that should be used for logging status
// messages while in the process of mounting.
//
// The returned writer must not be written to after calling SignalOutcome.
func StatusWriter() (w io.Writer) {
	panic("TODO")
}

// Invoke gcsfuse with the supplied arguments, waiting until it successfully
// mounts or reports that is has failed. Write status updates while mounting
// into the supplied writer (which may be nil for silence). Return nil only if
// it mounts successfully.
func Mount(
	gcsfusePath string,
	args []string,
	status io.Writer) (err error) {
	err = errors.New("TODO")
	return
}
<|endoftext|>


<commit_before>package registry

import (
	"archive/tar"
	"bytes"
	"errors"
	"fmt"
	"io"
	"path"

	"golang.org/x/net/context"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/remind101/empire"
	"github.com/remind101/empire/pkg/dockerutil"
	"github.com/remind101/empire/pkg/image"
	"github.com/remind101/empire/pkg/jsonmessage"
	"github.com/remind101/empire/procfile"
)

// dockerDaemon is an implementation of the empire.ImageRegistry interface
// backed by a local Docker daemon.
type dockerDaemon struct {
	docker    *dockerutil.Client
	extractor empire.ProcfileExtractor

	// can be set to false to disable Pull-before-Resolve.
	noPull bool
}

// DockerDaemon returns an empire.ImageRegistry that uses a local Docker Daemon
// to extract procfiles and resolve images.
func DockerDaemon(c *dockerutil.Client) empire.ImageRegistry {
	e := multiExtractor(
		newFileExtractor(c),
		newCMDExtractor(c),
	)
	return &dockerDaemon{
		docker:    c,
		extractor: e,
	}
}

func (r *dockerDaemon) ExtractProcfile(ctx context.Context, img image.Image, w *jsonmessage.Stream) ([]byte, error) {
	return r.extractor.ExtractProcfile(ctx, img, w)
}

func (r *dockerDaemon) Resolve(ctx context.Context, img image.Image, w *jsonmessage.Stream) (image.Image, error) {
	if !r.noPull {
		if err := r.docker.PullImage(ctx, docker.PullImageOptions{
			Registry:      img.Registry,
			Repository:    img.Repository,
			Tag:           img.Tag,
			OutputStream:  w,
			RawJSONStream: true,
		}); err != nil {
			return img, err
		}
	}

	// If the image already references an immutable identifier, there's
	// nothing for us to do.
	if img.Digest != "" {
		return img, nil
	}

	i, err := r.docker.InspectImage(img.String())
	if err != nil {
		return img, err
	}

	// If there are no repository digests (the case for Docker <= 1.11),
	// then we just fallback to the original identifier.
	if len(i.RepoDigests) <= 0 {
		w.Encode(jsonmessage.JSONMessage{
			Status: fmt.Sprintf("Status: Image has no repository digests. Using %s as image identifier", img),
		})
		return img, nil
	}

	digest := i.RepoDigests[0]

	w.Encode(jsonmessage.JSONMessage{
		Status: fmt.Sprintf("Status: Resolved %s to %s", img, digest),
	})

	return image.Decode(digest)
}

// cmdExtractor is an Extractor implementation that returns a Procfile based
// on the CMD directive in the Dockerfile. It makes the assumption that the cmd
// is a "web" process.
type cmdExtractor struct {
	// Client is the docker client to use to pull the container image.
	client *dockerutil.Client
}

func newCMDExtractor(c *dockerutil.Client) *cmdExtractor {
	return &cmdExtractor{client: c}
}

func (e *cmdExtractor) ExtractProcfile(ctx context.Context, img image.Image, w *jsonmessage.Stream) ([]byte, error) {
	i, err := e.client.InspectImage(img.String())
	if err != nil {
		return nil, err
	}

	w.Encode(jsonmessage.JSONMessage{
		Status: fmt.Sprintf("Status: Generating Procfile from CMD: %v", i.Config.Cmd),
	})

	return procfile.Marshal(procfile.ExtendedProcfile{
		"web": procfile.Process{
			Command: i.Config.Cmd,
		},
	})
}

// multiExtractor is an Extractor implementation that tries multiple Extractors
// in succession until one succeeds.
func multiExtractor(extractors ...empire.ProcfileExtractor) empire.ProcfileExtractor {
	return empire.ProcfileExtractorFunc(func(ctx context.Context, image image.Image, w *jsonmessage.Stream) ([]byte, error) {
		for _, extractor := range extractors {
			p, err := extractor.ExtractProcfile(ctx, image, w)

			// Yay!
			if err == nil {
				return p, nil
			}

			// Try the next one
			if _, ok := err.(*empire.ProcfileError); ok {
				continue
			}

			// Bubble up the error
			return p, err
		}

		return nil, &empire.ProcfileError{
			Err: errors.New("no suitable Procfile extractor found"),
		}
	})
}

// fileExtractor is an implementation of the Extractor interface that extracts
// the Procfile from the images WORKDIR.
type fileExtractor struct {
	// Client is the docker client to use to pull the container image.
	client *dockerutil.Client
}

func newFileExtractor(c *dockerutil.Client) *fileExtractor {
	return &fileExtractor{client: c}
}

// Extract implements Extractor Extract.
func (e *fileExtractor) ExtractProcfile(ctx context.Context, img image.Image, w *jsonmessage.Stream) ([]byte, error) {
	c, err := e.createContainer(ctx, img)
	if err != nil {
		return nil, err
	}

	defer e.removeContainer(ctx, c.ID)

	pfile, err := e.procfile(ctx, c.ID)
	if err != nil {
		return nil, err
	}

	b, err := e.copyFile(ctx, c.ID, pfile)
	if err != nil {
		return nil, &empire.ProcfileError{Err: err}
	}

	w.Encode(jsonmessage.JSONMessage{
		Status: fmt.Sprintf("Status: Extracted Procfile from %q", pfile),
	})

	return b, nil
}

// procfile returns the path to the Procfile. If the container has a WORKDIR
// set, then this will return a path to the Procfile within that directory.
func (e *fileExtractor) procfile(ctx context.Context, id string) (string, error) {
	p := ""

	c, err := e.client.InspectContainer(id)
	if err != nil {
		return "", err
	}

	if c.Config != nil {
		p = c.Config.WorkingDir
	}

	return path.Join(p, empire.Procfile), nil
}

// createContainer creates a new docker container for the given docker image.
func (e *fileExtractor) createContainer(ctx context.Context, img image.Image) (*docker.Container, error) {
	return e.client.CreateContainer(ctx, docker.CreateContainerOptions{
		Config: &docker.Config{
			Image: img.String(),
		},
	})
}

// removeContainer removes a container by its ID.
func (e *fileExtractor) removeContainer(ctx context.Context, containerID string) error {
	return e.client.RemoveContainer(ctx, docker.RemoveContainerOptions{
		ID: containerID,
	})
}

// copyFile copies a file from a container.
func (e *fileExtractor) copyFile(ctx context.Context, containerID, path string) ([]byte, error) {
	var buf bytes.Buffer
	if err := e.client.CopyFromContainer(ctx, docker.CopyFromContainerOptions{
		Container:    containerID,
		Resource:     path,
		OutputStream: &buf,
	}); err != nil {
		return nil, err
	}

	// Open the tar archive for reading.
	r := bytes.NewReader(buf.Bytes())

	return firstFile(tar.NewReader(r))
}

// firstFile extracts the first file from a tar archive.
func firstFile(tr *tar.Reader) ([]byte, error) {
	if _, err := tr.Next(); err != nil {
		return nil, err
	}

	var buf bytes.Buffer
	if _, err := io.Copy(&buf, tr); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}
<commit_msg>Fixes pull by digest<commit_after>package registry

import (
	"archive/tar"
	"bytes"
	"errors"
	"fmt"
	"io"
	"path"

	"golang.org/x/net/context"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/remind101/empire"
	"github.com/remind101/empire/pkg/dockerutil"
	"github.com/remind101/empire/pkg/image"
	"github.com/remind101/empire/pkg/jsonmessage"
	"github.com/remind101/empire/procfile"
)

// dockerDaemon is an implementation of the empire.ImageRegistry interface
// backed by a local Docker daemon.
type dockerDaemon struct {
	docker    *dockerutil.Client
	extractor empire.ProcfileExtractor

	// can be set to false to disable Pull-before-Resolve.
	noPull bool
}

// DockerDaemon returns an empire.ImageRegistry that uses a local Docker Daemon
// to extract procfiles and resolve images.
func DockerDaemon(c *dockerutil.Client) empire.ImageRegistry {
	e := multiExtractor(
		newFileExtractor(c),
		newCMDExtractor(c),
	)
	return &dockerDaemon{
		docker:    c,
		extractor: e,
	}
}

func (r *dockerDaemon) ExtractProcfile(ctx context.Context, img image.Image, w *jsonmessage.Stream) ([]byte, error) {
	return r.extractor.ExtractProcfile(ctx, img, w)
}

func (r *dockerDaemon) Resolve(ctx context.Context, img image.Image, w *jsonmessage.Stream) (image.Image, error) {
	if !r.noPull {
		// From the Docker API docs:
		//
		//	Tag or digest. If empty when pulling an image, this
		//	causes all tags for the given image to be pulled.
		//
		// So, we prefer the digest if it's provided.
		tag := img.Digest
		if tag == "" {
			tag = img.Tag
		}

		// If there's no tag or digest, error out. Providing an empty
		// tag to DockerPull will pull all images, which we don't want.
		if tag == "" {
			return img, fmt.Errorf("no tag or digest provided")
		}

		if err := r.docker.PullImage(ctx, docker.PullImageOptions{
			// Only required for Docker Engine 1.9 or 1.10 w/ Remote API < 1.21
			// and Docker Engine < 1.9
			// This parameter was removed in Docker Engine 1.11
			//
			// See https://goo.gl/9y9Bpx
			Registry: img.Registry,

			Repository:    img.Repository,
			Tag:           tag,
			OutputStream:  w,
			RawJSONStream: true,
		}); err != nil {
			return img, err
		}
	}

	// If the image already references an immutable identifier, there's
	// nothing for us to do.
	if img.Digest != "" {
		return img, nil
	}

	i, err := r.docker.InspectImage(img.String())
	if err != nil {
		return img, err
	}

	// If there are no repository digests (the case for Docker <= 1.11),
	// then we just fallback to the original identifier.
	if len(i.RepoDigests) <= 0 {
		w.Encode(jsonmessage.JSONMessage{
			Status: fmt.Sprintf("Status: Image has no repository digests. Using %s as image identifier", img),
		})
		return img, nil
	}

	digest := i.RepoDigests[0]

	w.Encode(jsonmessage.JSONMessage{
		Status: fmt.Sprintf("Status: Resolved %s to %s", img, digest),
	})

	return image.Decode(digest)
}

// cmdExtractor is an Extractor implementation that returns a Procfile based
// on the CMD directive in the Dockerfile. It makes the assumption that the cmd
// is a "web" process.
type cmdExtractor struct {
	// Client is the docker client to use to pull the container image.
	client *dockerutil.Client
}

func newCMDExtractor(c *dockerutil.Client) *cmdExtractor {
	return &cmdExtractor{client: c}
}

func (e *cmdExtractor) ExtractProcfile(ctx context.Context, img image.Image, w *jsonmessage.Stream) ([]byte, error) {
	i, err := e.client.InspectImage(img.String())
	if err != nil {
		return nil, err
	}

	w.Encode(jsonmessage.JSONMessage{
		Status: fmt.Sprintf("Status: Generating Procfile from CMD: %v", i.Config.Cmd),
	})

	return procfile.Marshal(procfile.ExtendedProcfile{
		"web": procfile.Process{
			Command: i.Config.Cmd,
		},
	})
}

// multiExtractor is an Extractor implementation that tries multiple Extractors
// in succession until one succeeds.
func multiExtractor(extractors ...empire.ProcfileExtractor) empire.ProcfileExtractor {
	return empire.ProcfileExtractorFunc(func(ctx context.Context, image image.Image, w *jsonmessage.Stream) ([]byte, error) {
		for _, extractor := range extractors {
			p, err := extractor.ExtractProcfile(ctx, image, w)

			// Yay!
			if err == nil {
				return p, nil
			}

			// Try the next one
			if _, ok := err.(*empire.ProcfileError); ok {
				continue
			}

			// Bubble up the error
			return p, err
		}

		return nil, &empire.ProcfileError{
			Err: errors.New("no suitable Procfile extractor found"),
		}
	})
}

// fileExtractor is an implementation of the Extractor interface that extracts
// the Procfile from the images WORKDIR.
type fileExtractor struct {
	// Client is the docker client to use to pull the container image.
	client *dockerutil.Client
}

func newFileExtractor(c *dockerutil.Client) *fileExtractor {
	return &fileExtractor{client: c}
}

// Extract implements Extractor Extract.
func (e *fileExtractor) ExtractProcfile(ctx context.Context, img image.Image, w *jsonmessage.Stream) ([]byte, error) {
	c, err := e.createContainer(ctx, img)
	if err != nil {
		return nil, err
	}

	defer e.removeContainer(ctx, c.ID)

	pfile, err := e.procfile(ctx, c.ID)
	if err != nil {
		return nil, err
	}

	b, err := e.copyFile(ctx, c.ID, pfile)
	if err != nil {
		return nil, &empire.ProcfileError{Err: err}
	}

	w.Encode(jsonmessage.JSONMessage{
		Status: fmt.Sprintf("Status: Extracted Procfile from %q", pfile),
	})

	return b, nil
}

// procfile returns the path to the Procfile. If the container has a WORKDIR
// set, then this will return a path to the Procfile within that directory.
func (e *fileExtractor) procfile(ctx context.Context, id string) (string, error) {
	p := ""

	c, err := e.client.InspectContainer(id)
	if err != nil {
		return "", err
	}

	if c.Config != nil {
		p = c.Config.WorkingDir
	}

	return path.Join(p, empire.Procfile), nil
}

// createContainer creates a new docker container for the given docker image.
func (e *fileExtractor) createContainer(ctx context.Context, img image.Image) (*docker.Container, error) {
	return e.client.CreateContainer(ctx, docker.CreateContainerOptions{
		Config: &docker.Config{
			Image: img.String(),
		},
	})
}

// removeContainer removes a container by its ID.
func (e *fileExtractor) removeContainer(ctx context.Context, containerID string) error {
	return e.client.RemoveContainer(ctx, docker.RemoveContainerOptions{
		ID: containerID,
	})
}

// copyFile copies a file from a container.
func (e *fileExtractor) copyFile(ctx context.Context, containerID, path string) ([]byte, error) {
	var buf bytes.Buffer
	if err := e.client.CopyFromContainer(ctx, docker.CopyFromContainerOptions{
		Container:    containerID,
		Resource:     path,
		OutputStream: &buf,
	}); err != nil {
		return nil, err
	}

	// Open the tar archive for reading.
	r := bytes.NewReader(buf.Bytes())

	return firstFile(tar.NewReader(r))
}

// firstFile extracts the first file from a tar archive.
func firstFile(tr *tar.Reader) ([]byte, error) {
	if _, err := tr.Next(); err != nil {
		return nil, err
	}

	var buf bytes.Buffer
	if _, err := io.Copy(&buf, tr); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}
<|endoftext|>


<commit_before>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package msgfmt implements a text marshaler combining the desirable features
// of both the JSON and proto text formats.
// It is optimized for human readability and has no associated deserializer.
package msgfmt

import (
	"bytes"
	"fmt"
	"math"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"time"

	"google.golang.org/protobuf/encoding/protowire"
	"google.golang.org/protobuf/internal/detrand"
	"google.golang.org/protobuf/internal/genid"
	"google.golang.org/protobuf/internal/mapsort"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/reflect/protoreflect"
	"google.golang.org/protobuf/reflect/protoregistry"
)

// Format returns a formatted string for the message.
func Format(m proto.Message) string {
	return string(appendMessage(nil, m.ProtoReflect()))
}

func appendValue(b []byte, v protoreflect.Value, fd protoreflect.FieldDescriptor) []byte {
	switch v := v.Interface().(type) {
	case bool, int32, int64, uint32, uint64, float32, float64:
		return append(b, fmt.Sprint(v)...)
	case string:
		return append(b, strconv.Quote(string(v))...)
	case []byte:
		return append(b, strconv.Quote(string(v))...)
	case protoreflect.EnumNumber:
		return appendEnum(b, v, fd.Enum())
	case protoreflect.Message:
		return appendMessage(b, v)
	case protoreflect.List:
		return appendList(b, v, fd)
	case protoreflect.Map:
		return appendMap(b, v, fd)
	default:
		panic(fmt.Sprintf("invalid type: %T", v))
	}
}

func appendEnum(b []byte, v protoreflect.EnumNumber, ed protoreflect.EnumDescriptor) []byte {
	if ev := ed.Values().ByNumber(v); ev != nil {
		return append(b, ev.Name()...)
	}
	return strconv.AppendInt(b, int64(v), 10)
}

func appendMessage(b []byte, m protoreflect.Message) []byte {
	if b2 := appendKnownMessage(b, m); b2 != nil {
		return b2
	}

	var fds []protoreflect.FieldDescriptor
	m.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
		fds = append(fds, fd)
		return true
	})
	sort.Slice(fds, func(i, j int) bool {
		fdi, fdj := fds[i], fds[j]
		switch {
		case !fdi.IsExtension() && !fdj.IsExtension():
			return fdi.Index() < fdj.Index()
		case fdi.IsExtension() && fdj.IsExtension():
			return fdi.FullName() < fdj.FullName()
		default:
			return !fdi.IsExtension() && fdj.IsExtension()
		}
	})

	b = append(b, '{')
	for _, fd := range fds {
		k := string(fd.Name())
		if fd.IsExtension() {
			k = string("[" + fd.FullName() + "]")
		}

		b = append(b, k...)
		b = append(b, ':')
		b = appendValue(b, m.Get(fd), fd)
		b = append(b, delim()...)
	}
	b = appendUnknown(b, m.GetUnknown())
	b = bytes.TrimRight(b, delim())
	b = append(b, '}')
	return b
}

var protocmpMessageType = reflect.TypeOf(map[string]interface{}(nil))

func appendKnownMessage(b []byte, m protoreflect.Message) []byte {
	md := m.Descriptor()
	fds := md.Fields()
	switch genid.WhichFile(md.FullName()) {
	case genid.Any_file:
		var msgVal protoreflect.Message
		url := m.Get(fds.ByName(genid.Any_TypeUrl_field_name)).String()
		if v := reflect.ValueOf(m); v.Type().ConvertibleTo(protocmpMessageType) {
			// For protocmp.Message, directly obtain the sub-message value
			// which is stored in structured form, rather than as raw bytes.
			m2 := v.Convert(protocmpMessageType).Interface().(map[string]interface{})
			v, ok := m2[string(genid.Any_Value_field_name)].(proto.Message)
			if !ok {
				return nil
			}
			msgVal = v.ProtoReflect()
		} else {
			val := m.Get(fds.ByName(genid.Any_Value_field_name)).Bytes()
			mt, err := protoregistry.GlobalTypes.FindMessageByURL(url)
			if err != nil {
				return nil
			}
			msgVal = mt.New()
			err = proto.UnmarshalOptions{AllowPartial: true}.Unmarshal(val, msgVal.Interface())
			if err != nil {
				return nil
			}
		}

		b = append(b, '{')
		b = append(b, "["+url+"]"...)
		b = append(b, ':')
		b = appendMessage(b, msgVal)
		b = append(b, '}')
		return b

	case genid.Timestamp_file:
		secs := m.Get(fds.ByName(genid.Timestamp_Seconds_field_name)).Int()
		nanos := m.Get(fds.ByName(genid.Timestamp_Nanos_field_name)).Int()
		if nanos < 0 || nanos >= 1e9 {
			return nil
		}
		t := time.Unix(secs, nanos).UTC()
		x := t.Format("2006-01-02T15:04:05.000000000") // RFC 3339
		x = strings.TrimSuffix(x, "000")
		x = strings.TrimSuffix(x, "000")
		x = strings.TrimSuffix(x, ".000")
		return append(b, x+"Z"...)

	case genid.Duration_file:
		secs := m.Get(fds.ByName(genid.Duration_Seconds_field_name)).Int()
		nanos := m.Get(fds.ByName(genid.Duration_Nanos_field_name)).Int()
		if nanos <= -1e9 || nanos >= 1e9 || (secs > 0 && nanos < 0) || (secs < 0 && nanos > 0) {
			return nil
		}
		x := fmt.Sprintf("%d.%09d", secs, int64(math.Abs(float64(nanos))))
		x = strings.TrimSuffix(x, "000")
		x = strings.TrimSuffix(x, "000")
		x = strings.TrimSuffix(x, ".000")
		return append(b, x+"s"...)

	case genid.Wrappers_file:
		fd := fds.ByName(genid.WrapperValue_Value_field_name)
		return appendValue(b, m.Get(fd), fd)
	}

	return nil
}

func appendUnknown(b []byte, raw protoreflect.RawFields) []byte {
	rs := make(map[protoreflect.FieldNumber][]protoreflect.RawFields)
	for len(raw) > 0 {
		num, _, n := protowire.ConsumeField(raw)
		rs[num] = append(rs[num], raw[:n])
		raw = raw[n:]
	}

	var ns []protoreflect.FieldNumber
	for n := range rs {
		ns = append(ns, n)
	}
	sort.Slice(ns, func(i, j int) bool { return ns[i] < ns[j] })

	for _, n := range ns {
		var leftBracket, rightBracket string
		if len(rs[n]) > 1 {
			leftBracket, rightBracket = "[", "]"
		}

		b = strconv.AppendInt(b, int64(n), 10)
		b = append(b, ':')
		b = append(b, leftBracket...)
		for _, r := range rs[n] {
			num, typ, n := protowire.ConsumeTag(r)
			r = r[n:]
			switch typ {
			case protowire.VarintType:
				v, _ := protowire.ConsumeVarint(r)
				b = strconv.AppendInt(b, int64(v), 10)
			case protowire.Fixed32Type:
				v, _ := protowire.ConsumeFixed32(r)
				b = append(b, fmt.Sprintf("0x%08x", v)...)
			case protowire.Fixed64Type:
				v, _ := protowire.ConsumeFixed64(r)
				b = append(b, fmt.Sprintf("0x%016x", v)...)
			case protowire.BytesType:
				v, _ := protowire.ConsumeBytes(r)
				b = strconv.AppendQuote(b, string(v))
			case protowire.StartGroupType:
				v, _ := protowire.ConsumeGroup(num, r)
				b = append(b, '{')
				b = appendUnknown(b, v)
				b = bytes.TrimRight(b, delim())
				b = append(b, '}')
			default:
				panic(fmt.Sprintf("invalid type: %v", typ))
			}
			b = append(b, delim()...)
		}
		b = bytes.TrimRight(b, delim())
		b = append(b, rightBracket...)
		b = append(b, delim()...)
	}
	return b
}

func appendList(b []byte, v protoreflect.List, fd protoreflect.FieldDescriptor) []byte {
	b = append(b, '[')
	for i := 0; i < v.Len(); i++ {
		b = appendValue(b, v.Get(i), fd)
		b = append(b, delim()...)
	}
	b = bytes.TrimRight(b, delim())
	b = append(b, ']')
	return b
}

func appendMap(b []byte, v protoreflect.Map, fd protoreflect.FieldDescriptor) []byte {
	var ks []protoreflect.MapKey
	mapsort.Range(v, fd.MapKey().Kind(), func(k protoreflect.MapKey, _ protoreflect.Value) bool {
		ks = append(ks, k)
		return true
	})

	b = append(b, '{')
	for _, k := range ks {
		b = appendValue(b, k.Value(), fd.MapKey())
		b = append(b, ':')
		b = appendValue(b, v.Get(k), fd.MapValue())
		b = append(b, delim()...)
	}
	b = bytes.TrimRight(b, delim())
	b = append(b, '}')
	return b
}

func delim() string {
	// Deliberately introduce instability into the message string to
	// discourage users from depending on it.
	if detrand.Bool() {
		return "  "
	}
	return ", "
}
<commit_msg>internal/msgfmt: adjust handling of well-known types<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package msgfmt implements a text marshaler combining the desirable features
// of both the JSON and proto text formats.
// It is optimized for human readability and has no associated deserializer.
package msgfmt

import (
	"bytes"
	"fmt"
	"math"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"time"

	"google.golang.org/protobuf/encoding/protowire"
	"google.golang.org/protobuf/internal/detrand"
	"google.golang.org/protobuf/internal/genid"
	"google.golang.org/protobuf/internal/mapsort"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/reflect/protoreflect"
	"google.golang.org/protobuf/reflect/protoregistry"
)

// Format returns a formatted string for the message.
func Format(m proto.Message) string {
	return string(appendMessage(nil, m.ProtoReflect()))
}

func appendValue(b []byte, v protoreflect.Value, fd protoreflect.FieldDescriptor) []byte {
	switch v := v.Interface().(type) {
	case bool, int32, int64, uint32, uint64, float32, float64:
		return append(b, fmt.Sprint(v)...)
	case string:
		return append(b, strconv.Quote(string(v))...)
	case []byte:
		return append(b, strconv.Quote(string(v))...)
	case protoreflect.EnumNumber:
		return appendEnum(b, v, fd.Enum())
	case protoreflect.Message:
		return appendMessage(b, v)
	case protoreflect.List:
		return appendList(b, v, fd)
	case protoreflect.Map:
		return appendMap(b, v, fd)
	default:
		panic(fmt.Sprintf("invalid type: %T", v))
	}
}

func appendEnum(b []byte, v protoreflect.EnumNumber, ed protoreflect.EnumDescriptor) []byte {
	if ev := ed.Values().ByNumber(v); ev != nil {
		return append(b, ev.Name()...)
	}
	return strconv.AppendInt(b, int64(v), 10)
}

func appendMessage(b []byte, m protoreflect.Message) []byte {
	if b2 := appendKnownMessage(b, m); b2 != nil {
		return b2
	}

	var fds []protoreflect.FieldDescriptor
	m.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
		fds = append(fds, fd)
		return true
	})
	sort.Slice(fds, func(i, j int) bool {
		fdi, fdj := fds[i], fds[j]
		switch {
		case !fdi.IsExtension() && !fdj.IsExtension():
			return fdi.Index() < fdj.Index()
		case fdi.IsExtension() && fdj.IsExtension():
			return fdi.FullName() < fdj.FullName()
		default:
			return !fdi.IsExtension() && fdj.IsExtension()
		}
	})

	b = append(b, '{')
	for _, fd := range fds {
		k := string(fd.Name())
		if fd.IsExtension() {
			k = string("[" + fd.FullName() + "]")
		}

		b = append(b, k...)
		b = append(b, ':')
		b = appendValue(b, m.Get(fd), fd)
		b = append(b, delim()...)
	}
	b = appendUnknown(b, m.GetUnknown())
	b = bytes.TrimRight(b, delim())
	b = append(b, '}')
	return b
}

var protocmpMessageType = reflect.TypeOf(map[string]interface{}(nil))

func appendKnownMessage(b []byte, m protoreflect.Message) []byte {
	md := m.Descriptor()
	fds := md.Fields()
	switch md.FullName() {
	case genid.Any_message_fullname:
		var msgVal protoreflect.Message
		url := m.Get(fds.ByNumber(genid.Any_TypeUrl_field_number)).String()
		if v := reflect.ValueOf(m); v.Type().ConvertibleTo(protocmpMessageType) {
			// For protocmp.Message, directly obtain the sub-message value
			// which is stored in structured form, rather than as raw bytes.
			m2 := v.Convert(protocmpMessageType).Interface().(map[string]interface{})
			v, ok := m2[string(genid.Any_Value_field_name)].(proto.Message)
			if !ok {
				return nil
			}
			msgVal = v.ProtoReflect()
		} else {
			val := m.Get(fds.ByNumber(genid.Any_Value_field_number)).Bytes()
			mt, err := protoregistry.GlobalTypes.FindMessageByURL(url)
			if err != nil {
				return nil
			}
			msgVal = mt.New()
			err = proto.UnmarshalOptions{AllowPartial: true}.Unmarshal(val, msgVal.Interface())
			if err != nil {
				return nil
			}
		}

		b = append(b, '{')
		b = append(b, "["+url+"]"...)
		b = append(b, ':')
		b = appendMessage(b, msgVal)
		b = append(b, '}')
		return b

	case genid.Timestamp_message_fullname:
		secs := m.Get(fds.ByNumber(genid.Timestamp_Seconds_field_number)).Int()
		nanos := m.Get(fds.ByNumber(genid.Timestamp_Nanos_field_number)).Int()
		if nanos < 0 || nanos >= 1e9 {
			return nil
		}
		t := time.Unix(secs, nanos).UTC()
		x := t.Format("2006-01-02T15:04:05.000000000") // RFC 3339
		x = strings.TrimSuffix(x, "000")
		x = strings.TrimSuffix(x, "000")
		x = strings.TrimSuffix(x, ".000")
		return append(b, x+"Z"...)

	case genid.Duration_message_fullname:
		secs := m.Get(fds.ByNumber(genid.Duration_Seconds_field_number)).Int()
		nanos := m.Get(fds.ByNumber(genid.Duration_Nanos_field_number)).Int()
		if nanos <= -1e9 || nanos >= 1e9 || (secs > 0 && nanos < 0) || (secs < 0 && nanos > 0) {
			return nil
		}
		x := fmt.Sprintf("%d.%09d", secs, int64(math.Abs(float64(nanos))))
		x = strings.TrimSuffix(x, "000")
		x = strings.TrimSuffix(x, "000")
		x = strings.TrimSuffix(x, ".000")
		return append(b, x+"s"...)
	}

	if genid.WhichFile(md.FullName()) == genid.Wrappers_file {
		fd := fds.ByNumber(genid.WrapperValue_Value_field_number)
		return appendValue(b, m.Get(fd), fd)
	}

	return nil
}

func appendUnknown(b []byte, raw protoreflect.RawFields) []byte {
	rs := make(map[protoreflect.FieldNumber][]protoreflect.RawFields)
	for len(raw) > 0 {
		num, _, n := protowire.ConsumeField(raw)
		rs[num] = append(rs[num], raw[:n])
		raw = raw[n:]
	}

	var ns []protoreflect.FieldNumber
	for n := range rs {
		ns = append(ns, n)
	}
	sort.Slice(ns, func(i, j int) bool { return ns[i] < ns[j] })

	for _, n := range ns {
		var leftBracket, rightBracket string
		if len(rs[n]) > 1 {
			leftBracket, rightBracket = "[", "]"
		}

		b = strconv.AppendInt(b, int64(n), 10)
		b = append(b, ':')
		b = append(b, leftBracket...)
		for _, r := range rs[n] {
			num, typ, n := protowire.ConsumeTag(r)
			r = r[n:]
			switch typ {
			case protowire.VarintType:
				v, _ := protowire.ConsumeVarint(r)
				b = strconv.AppendInt(b, int64(v), 10)
			case protowire.Fixed32Type:
				v, _ := protowire.ConsumeFixed32(r)
				b = append(b, fmt.Sprintf("0x%08x", v)...)
			case protowire.Fixed64Type:
				v, _ := protowire.ConsumeFixed64(r)
				b = append(b, fmt.Sprintf("0x%016x", v)...)
			case protowire.BytesType:
				v, _ := protowire.ConsumeBytes(r)
				b = strconv.AppendQuote(b, string(v))
			case protowire.StartGroupType:
				v, _ := protowire.ConsumeGroup(num, r)
				b = append(b, '{')
				b = appendUnknown(b, v)
				b = bytes.TrimRight(b, delim())
				b = append(b, '}')
			default:
				panic(fmt.Sprintf("invalid type: %v", typ))
			}
			b = append(b, delim()...)
		}
		b = bytes.TrimRight(b, delim())
		b = append(b, rightBracket...)
		b = append(b, delim()...)
	}
	return b
}

func appendList(b []byte, v protoreflect.List, fd protoreflect.FieldDescriptor) []byte {
	b = append(b, '[')
	for i := 0; i < v.Len(); i++ {
		b = appendValue(b, v.Get(i), fd)
		b = append(b, delim()...)
	}
	b = bytes.TrimRight(b, delim())
	b = append(b, ']')
	return b
}

func appendMap(b []byte, v protoreflect.Map, fd protoreflect.FieldDescriptor) []byte {
	var ks []protoreflect.MapKey
	mapsort.Range(v, fd.MapKey().Kind(), func(k protoreflect.MapKey, _ protoreflect.Value) bool {
		ks = append(ks, k)
		return true
	})

	b = append(b, '{')
	for _, k := range ks {
		b = appendValue(b, k.Value(), fd.MapKey())
		b = append(b, ':')
		b = appendValue(b, v.Get(k), fd.MapValue())
		b = append(b, delim()...)
	}
	b = bytes.TrimRight(b, delim())
	b = append(b, '}')
	return b
}

func delim() string {
	// Deliberately introduce instability into the message string to
	// discourage users from depending on it.
	if detrand.Bool() {
		return "  "
	}
	return ", "
}
<|endoftext|>


<commit_before>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package socket provides a portable interface for socket system
// calls.
package socket // import "golang.org/x/net/internal/socket"

import (
	"errors"
	"net"
	"unsafe"
)

// An Option represents a sticky socket option.
type Option struct {
	Level int // level
	Name  int // name; must be equal or greater than 1
	Len   int // length of value in bytes; must be equal or greater than 1
}

// Get reads a value for the option from the kernel.
// It returns the number of bytes written into b.
func (o *Option) Get(c *Conn, b []byte) (int, error) {
	if o.Name < 1 || o.Len < 1 {
		return 0, errors.New("invalid option")
	}
	if len(b) < o.Len {
		return 0, errors.New("short buffer")
	}
	return o.get(c, b)
}

// GetInt returns an integer value for the option.
//
// The Len field of Option must be either 1 or 4.
func (o *Option) GetInt(c *Conn) (int, error) {
	if o.Len != 1 && o.Len != 4 {
		return 0, errors.New("invalid option")
	}
	var b []byte
	var bb [4]byte
	if o.Len == 1 {
		b = bb[:1]
	} else {
		b = bb[:4]
	}
	n, err := o.get(c, b)
	if err != nil {
		return 0, err
	}
	if n != o.Len {
		return 0, errors.New("invalid option length")
	}
	if o.Len == 1 {
		return int(b[0]), nil
	}
	return int(NativeEndian.Uint32(b[:4])), nil
}

// Set writes the option and value to the kernel.
func (o *Option) Set(c *Conn, b []byte) error {
	if o.Name < 1 || o.Len < 1 {
		return errors.New("invalid option")
	}
	if len(b) < o.Len {
		return errors.New("short buffer")
	}
	return o.set(c, b)
}

// SetInt writes the option and value to the kernel.
//
// The Len field of Option must be either 1 or 4.
func (o *Option) SetInt(c *Conn, v int) error {
	if o.Len != 1 && o.Len != 4 {
		return errors.New("invalid option")
	}
	var b []byte
	if o.Len == 1 {
		b = []byte{byte(v)}
	} else {
		var bb [4]byte
		NativeEndian.PutUint32(bb[:o.Len], uint32(v))
		b = bb[:4]
	}
	return o.set(c, b)
}

func controlHeaderLen() int {
	return roundup(sizeofCmsghdr)
}

func controlMessageLen(dataLen int) int {
	return roundup(sizeofCmsghdr) + dataLen
}

// ControlMessageSpace returns the whole length of control message.
func ControlMessageSpace(dataLen int) int {
	return roundup(sizeofCmsghdr) + roundup(dataLen)
}

// A ControlMessage represents the head message in a stream of control
// messages.
//
// A control message comprises of a header, data and a few padding
// fields to conform to the interface to the kernel.
//
// See RFC 3542 for further information.
type ControlMessage []byte

// Data returns the data field of the control message at the head on
// w.
func (m ControlMessage) Data(dataLen int) []byte {
	l := controlHeaderLen()
	if len(m) < l || len(m) < l+dataLen {
		return nil
	}
	return m[l : l+dataLen]
}

// Next returns the control message at the next on w.
//
// Next works only for standard control messages.
func (m ControlMessage) Next(dataLen int) ControlMessage {
	l := ControlMessageSpace(dataLen)
	if len(m) < l {
		return nil
	}
	return m[l:]
}

// MarshalHeader marshals the header fields of the control message at
// the head on w.
func (m ControlMessage) MarshalHeader(lvl, typ, dataLen int) error {
	if len(m) < controlHeaderLen() {
		return errors.New("short message")
	}
	h := (*cmsghdr)(unsafe.Pointer(&m[0]))
	h.set(controlMessageLen(dataLen), lvl, typ)
	return nil
}

// ParseHeader parses and returns the header fields of the control
// message at the head on w.
func (m ControlMessage) ParseHeader() (lvl, typ, dataLen int, err error) {
	l := controlHeaderLen()
	if len(m) < l {
		return 0, 0, 0, errors.New("short message")
	}
	h := (*cmsghdr)(unsafe.Pointer(&m[0]))
	return h.lvl(), h.typ(), int(uint64(h.len()) - uint64(l)), nil
}

// Marshal marshals the control message at the head on w, and returns
// the next control message.
func (m ControlMessage) Marshal(lvl, typ int, data []byte) (ControlMessage, error) {
	l := len(data)
	if len(m) < ControlMessageSpace(l) {
		return nil, errors.New("short message")
	}
	h := (*cmsghdr)(unsafe.Pointer(&m[0]))
	h.set(controlMessageLen(l), lvl, typ)
	if l > 0 {
		copy(m.Data(l), data)
	}
	return m.Next(l), nil
}

// Parse parses w as a single or multiple control messages.
//
// Parse works for both standard and compatible messages.
func (m ControlMessage) Parse() ([]ControlMessage, error) {
	var ms []ControlMessage
	for len(m) >= controlHeaderLen() {
		h := (*cmsghdr)(unsafe.Pointer(&m[0]))
		l := h.len()
		if l <= 0 {
			return nil, errors.New("invalid header length")
		}
		if uint64(l) < uint64(controlHeaderLen()) {
			return nil, errors.New("invalid message length")
		}
		if uint64(l) > uint64(len(m)) {
			return nil, errors.New("short buffer")
		}
		// On message reception:
		//
		// |<- ControlMessageSpace --------------->|
		// |<- controlMessageLen ---------->|      |
		// |<- controlHeaderLen ->|         |      |
		// +---------------+------+---------+------+
		// |    Header     | PadH |  Data   | PadD |
		// +---------------+------+---------+------+
		//
		// On compatible message reception:
		//
		// | ... |<- controlMessageLen ----------->|
		// | ... |<- controlHeaderLen ->|          |
		// +-----+---------------+------+----------+
		// | ... |    Header     | PadH |   Data   |
		// +-----+---------------+------+----------+
		ms = append(ms, ControlMessage(m[:l]))
		ll := l - controlHeaderLen()
		if len(m) >= ControlMessageSpace(ll) {
			m = m[ControlMessageSpace(ll):]
		} else {
			m = m[controlMessageLen(ll):]
		}
	}
	return ms, nil
}

// NewControlMessage returns a new stream of control messages.
func NewControlMessage(dataLen []int) ControlMessage {
	var l int
	for i := range dataLen {
		l += ControlMessageSpace(dataLen[i])
	}
	return make([]byte, l)
}

// A Message represents an IO message.
type Message struct {
	// When writing, the Buffers field must contain at least one
	// byte to write.
	// When reading, the Buffers field will always contain a byte
	// to read.
	Buffers [][]byte

	// OOB contains protocol-specific control or miscellaneous
	// ancillary data known as out-of-band data.
	OOB []byte

	// Addr specifies a destination address when writing.
	// It can be nil when the underlying protocol of the raw
	// connection uses connection-oriented communication.
	// After a successful read, it may contain the source address
	// on the received packet.
	Addr net.Addr

	N     int // # of bytes read or written from/to Buffers
	NN    int // # of bytes read or written from/to OOB
	Flags int // protocol-specific information on the received message
}

// RecvMsg wraps recvmsg system call.
//
// The provided flags is a set of platform-dependent flags, such as
// syscall.MSG_PEEK.
func (c *Conn) RecvMsg(m *Message, flags int) error {
	return c.recvMsg(m, flags)
}

// SendMsg wraps sendmsg system call.
//
// The provided flags is a set of platform-dependent flags, such as
// syscall.MSG_DONTROUTE.
func (c *Conn) SendMsg(m *Message, flags int) error {
	return c.sendMsg(m, flags)
}

// RecvMsgs wraps recvmmsg system call.
//
// It returns the number of processed messages.
//
// The provided flags is a set of platform-dependent flags, such as
// syscall.MSG_PEEK.
//
// Only Linux supports this.
func (c *Conn) RecvMsgs(ms []Message, flags int) (int, error) {
	return c.recvMsgs(ms, flags)
}

// SendMsgs wraps sendmmsg system call.
//
// It returns the number of processed messages.
//
// The provided flags is a set of platform-dependent flags, such as
// syscall.MSG_DONTROUTE.
//
// Only Linux supports this.
func (c *Conn) SendMsgs(ms []Message, flags int) (int, error) {
	return c.sendMsgs(ms, flags)
}
<commit_msg>internal/socket: fix typos<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package socket provides a portable interface for socket system
// calls.
package socket // import "golang.org/x/net/internal/socket"

import (
	"errors"
	"net"
	"unsafe"
)

// An Option represents a sticky socket option.
type Option struct {
	Level int // level
	Name  int // name; must be equal or greater than 1
	Len   int // length of value in bytes; must be equal or greater than 1
}

// Get reads a value for the option from the kernel.
// It returns the number of bytes written into b.
func (o *Option) Get(c *Conn, b []byte) (int, error) {
	if o.Name < 1 || o.Len < 1 {
		return 0, errors.New("invalid option")
	}
	if len(b) < o.Len {
		return 0, errors.New("short buffer")
	}
	return o.get(c, b)
}

// GetInt returns an integer value for the option.
//
// The Len field of Option must be either 1 or 4.
func (o *Option) GetInt(c *Conn) (int, error) {
	if o.Len != 1 && o.Len != 4 {
		return 0, errors.New("invalid option")
	}
	var b []byte
	var bb [4]byte
	if o.Len == 1 {
		b = bb[:1]
	} else {
		b = bb[:4]
	}
	n, err := o.get(c, b)
	if err != nil {
		return 0, err
	}
	if n != o.Len {
		return 0, errors.New("invalid option length")
	}
	if o.Len == 1 {
		return int(b[0]), nil
	}
	return int(NativeEndian.Uint32(b[:4])), nil
}

// Set writes the option and value to the kernel.
func (o *Option) Set(c *Conn, b []byte) error {
	if o.Name < 1 || o.Len < 1 {
		return errors.New("invalid option")
	}
	if len(b) < o.Len {
		return errors.New("short buffer")
	}
	return o.set(c, b)
}

// SetInt writes the option and value to the kernel.
//
// The Len field of Option must be either 1 or 4.
func (o *Option) SetInt(c *Conn, v int) error {
	if o.Len != 1 && o.Len != 4 {
		return errors.New("invalid option")
	}
	var b []byte
	if o.Len == 1 {
		b = []byte{byte(v)}
	} else {
		var bb [4]byte
		NativeEndian.PutUint32(bb[:o.Len], uint32(v))
		b = bb[:4]
	}
	return o.set(c, b)
}

func controlHeaderLen() int {
	return roundup(sizeofCmsghdr)
}

func controlMessageLen(dataLen int) int {
	return roundup(sizeofCmsghdr) + dataLen
}

// ControlMessageSpace returns the whole length of control message.
func ControlMessageSpace(dataLen int) int {
	return roundup(sizeofCmsghdr) + roundup(dataLen)
}

// A ControlMessage represents the head message in a stream of control
// messages.
//
// A control message comprises of a header, data and a few padding
// fields to conform to the interface to the kernel.
//
// See RFC 3542 for further information.
type ControlMessage []byte

// Data returns the data field of the control message at the head on
// m.
func (m ControlMessage) Data(dataLen int) []byte {
	l := controlHeaderLen()
	if len(m) < l || len(m) < l+dataLen {
		return nil
	}
	return m[l : l+dataLen]
}

// Next returns the control message at the next on m.
//
// Next works only for standard control messages.
func (m ControlMessage) Next(dataLen int) ControlMessage {
	l := ControlMessageSpace(dataLen)
	if len(m) < l {
		return nil
	}
	return m[l:]
}

// MarshalHeader marshals the header fields of the control message at
// the head on m.
func (m ControlMessage) MarshalHeader(lvl, typ, dataLen int) error {
	if len(m) < controlHeaderLen() {
		return errors.New("short message")
	}
	h := (*cmsghdr)(unsafe.Pointer(&m[0]))
	h.set(controlMessageLen(dataLen), lvl, typ)
	return nil
}

// ParseHeader parses and returns the header fields of the control
// message at the head on m.
func (m ControlMessage) ParseHeader() (lvl, typ, dataLen int, err error) {
	l := controlHeaderLen()
	if len(m) < l {
		return 0, 0, 0, errors.New("short message")
	}
	h := (*cmsghdr)(unsafe.Pointer(&m[0]))
	return h.lvl(), h.typ(), int(uint64(h.len()) - uint64(l)), nil
}

// Marshal marshals the control message at the head on m, and returns
// the next control message.
func (m ControlMessage) Marshal(lvl, typ int, data []byte) (ControlMessage, error) {
	l := len(data)
	if len(m) < ControlMessageSpace(l) {
		return nil, errors.New("short message")
	}
	h := (*cmsghdr)(unsafe.Pointer(&m[0]))
	h.set(controlMessageLen(l), lvl, typ)
	if l > 0 {
		copy(m.Data(l), data)
	}
	return m.Next(l), nil
}

// Parse parses m as a single or multiple control messages.
//
// Parse works for both standard and compatible messages.
func (m ControlMessage) Parse() ([]ControlMessage, error) {
	var ms []ControlMessage
	for len(m) >= controlHeaderLen() {
		h := (*cmsghdr)(unsafe.Pointer(&m[0]))
		l := h.len()
		if l <= 0 {
			return nil, errors.New("invalid header length")
		}
		if uint64(l) < uint64(controlHeaderLen()) {
			return nil, errors.New("invalid message length")
		}
		if uint64(l) > uint64(len(m)) {
			return nil, errors.New("short buffer")
		}
		// On message reception:
		//
		// |<- ControlMessageSpace --------------->|
		// |<- controlMessageLen ---------->|      |
		// |<- controlHeaderLen ->|         |      |
		// +---------------+------+---------+------+
		// |    Header     | PadH |  Data   | PadD |
		// +---------------+------+---------+------+
		//
		// On compatible message reception:
		//
		// | ... |<- controlMessageLen ----------->|
		// | ... |<- controlHeaderLen ->|          |
		// +-----+---------------+------+----------+
		// | ... |    Header     | PadH |   Data   |
		// +-----+---------------+------+----------+
		ms = append(ms, ControlMessage(m[:l]))
		ll := l - controlHeaderLen()
		if len(m) >= ControlMessageSpace(ll) {
			m = m[ControlMessageSpace(ll):]
		} else {
			m = m[controlMessageLen(ll):]
		}
	}
	return ms, nil
}

// NewControlMessage returns a new stream of control messages.
func NewControlMessage(dataLen []int) ControlMessage {
	var l int
	for i := range dataLen {
		l += ControlMessageSpace(dataLen[i])
	}
	return make([]byte, l)
}

// A Message represents an IO message.
type Message struct {
	// When writing, the Buffers field must contain at least one
	// byte to write.
	// When reading, the Buffers field will always contain a byte
	// to read.
	Buffers [][]byte

	// OOB contains protocol-specific control or miscellaneous
	// ancillary data known as out-of-band data.
	OOB []byte

	// Addr specifies a destination address when writing.
	// It can be nil when the underlying protocol of the raw
	// connection uses connection-oriented communication.
	// After a successful read, it may contain the source address
	// on the received packet.
	Addr net.Addr

	N     int // # of bytes read or written from/to Buffers
	NN    int // # of bytes read or written from/to OOB
	Flags int // protocol-specific information on the received message
}

// RecvMsg wraps recvmsg system call.
//
// The provided flags is a set of platform-dependent flags, such as
// syscall.MSG_PEEK.
func (c *Conn) RecvMsg(m *Message, flags int) error {
	return c.recvMsg(m, flags)
}

// SendMsg wraps sendmsg system call.
//
// The provided flags is a set of platform-dependent flags, such as
// syscall.MSG_DONTROUTE.
func (c *Conn) SendMsg(m *Message, flags int) error {
	return c.sendMsg(m, flags)
}

// RecvMsgs wraps recvmmsg system call.
//
// It returns the number of processed messages.
//
// The provided flags is a set of platform-dependent flags, such as
// syscall.MSG_PEEK.
//
// Only Linux supports this.
func (c *Conn) RecvMsgs(ms []Message, flags int) (int, error) {
	return c.recvMsgs(ms, flags)
}

// SendMsgs wraps sendmmsg system call.
//
// It returns the number of processed messages.
//
// The provided flags is a set of platform-dependent flags, such as
// syscall.MSG_DONTROUTE.
//
// Only Linux supports this.
func (c *Conn) SendMsgs(ms []Message, flags int) (int, error) {
	return c.sendMsgs(ms, flags)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuseutil

import (
	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseops"
)

// A FileSystem that responds to all ops with fuse.ENOSYS. Embed this in your
// struct to inherit default implementations for the methods you don't care
// about, ensuring your struct will continue to implement FileSystem even as
// new methods are added.
type NotImplementedFileSystem struct {
}

var _ FileSystem = &NotImplementedFileSystem{}

func (fs *NotImplementedFileSystem) Init(
	op *fuseops.InitOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) LookUpInode(
	op *fuseops.LookUpInodeOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) GetInodeAttributes(
	op *fuseops.GetInodeAttributesOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) SetInodeAttributes(
	op *fuseops.SetInodeAttributesOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) ForgetInode(
	op *fuseops.ForgetInodeOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) MkDir(
	op *fuseops.MkDirOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) CreateFile(
	op *fuseops.CreateFileOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) CreateSymlink(
	op *fuseops.CreateSymlinkOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) RmDir(
	op *fuseops.RmDirOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) Unlink(
	op *fuseops.UnlinkOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) OpenDir(
	op *fuseops.OpenDirOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) ReadDir(
	op *fuseops.ReadDirOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) ReleaseDirHandle(
	op *fuseops.ReleaseDirHandleOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) OpenFile(
	op *fuseops.OpenFileOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) ReadFile(
	op *fuseops.ReadFileOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) WriteFile(
	op *fuseops.WriteFileOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) SyncFile(
	op *fuseops.SyncFileOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) FlushFile(
	op *fuseops.FlushFileOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) ReleaseFileHandle(
	op *fuseops.ReleaseFileHandleOp) {
	op.Respond(fuse.ENOSYS)
}

func (fs *NotImplementedFileSystem) ReadSymlink(
	op *fuseops.ReadSymlinkOp) {
	op.Respond(fuse.ENOSYS)
}
<commit_msg>Fixed NotImplementedFileSystem.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuseutil

import (
	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseops"
)

// A FileSystem that responds to all ops with fuse.ENOSYS. Embed this in your
// struct to inherit default implementations for the methods you don't care
// about, ensuring your struct will continue to implement FileSystem even as
// new methods are added.
type NotImplementedFileSystem struct {
}

var _ FileSystem = &NotImplementedFileSystem{}

func (fs *NotImplementedFileSystem) Init(
	op *fuseops.InitOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) LookUpInode(
	op *fuseops.LookUpInodeOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) GetInodeAttributes(
	op *fuseops.GetInodeAttributesOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) SetInodeAttributes(
	op *fuseops.SetInodeAttributesOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) ForgetInode(
	op *fuseops.ForgetInodeOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) MkDir(
	op *fuseops.MkDirOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) CreateFile(
	op *fuseops.CreateFileOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) CreateSymlink(
	op *fuseops.CreateSymlinkOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) RmDir(
	op *fuseops.RmDirOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) Unlink(
	op *fuseops.UnlinkOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) OpenDir(
	op *fuseops.OpenDirOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) ReadDir(
	op *fuseops.ReadDirOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) ReleaseDirHandle(
	op *fuseops.ReleaseDirHandleOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) OpenFile(
	op *fuseops.OpenFileOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) ReadFile(
	op *fuseops.ReadFileOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) WriteFile(
	op *fuseops.WriteFileOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) SyncFile(
	op *fuseops.SyncFileOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) FlushFile(
	op *fuseops.FlushFileOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) ReleaseFileHandle(
	op *fuseops.ReleaseFileHandleOp) (err error) {
	err = fuse.ENOSYS
	return
}

func (fs *NotImplementedFileSystem) ReadSymlink(
	op *fuseops.ReadSymlinkOp) (err error) {
	err = fuse.ENOSYS
	return
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package gcstesting

import "github.com/jacobsa/gcloud/gcs"

// An interface that all bucket tests must implement.
type bucketTestSetUpInterface interface {
	SetUpBucketTest(b gcs.Bucket)
}

func registerTestSuite(
	makeBucket func() gcs.Bucket,
	prototype bucketTestSetUpInterface)

// Given a function that returns an initialized, empty bucket, register test
// suites that exercise the buckets returned by the function with ogletest.
func RegisterBucketTests(makeBucket func() gcs.Bucket) {
	// A list of empty instances of the test suites we want to register.
	suitePrototypes := []bucketTestSetUpInterface{
		&createTest{},
		&readTest{},
		&deleteTest{},
		&listTest{},
	}

	// Register each.
	for _, suitePrototype := range suitePrototypes {
		registerTestSuite(makeBucket, suitePrototype)
	}
}
<commit_msg>Implemented registerTestSuite.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package gcstesting

import (
	"reflect"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/ogletest"
)

// An interface that all bucket tests must implement.
type bucketTestSetUpInterface interface {
	SetUpBucketTest(b gcs.Bucket)
}

func getSuiteName(prototype interface{}) string

func getTestMethods(suitePrototype interface{}) []reflect.Method

func registerTestSuite(
	makeBucket func() gcs.Bucket,
	prototype bucketTestSetUpInterface) {
	suiteType := reflect.TypeOf(prototype)

	// We don't need anything fancy at the suite level.
	var ts ogletest.TestSuite
	ts.Name = getSuiteName(prototype)

	// For each method, we create a test function.
	for _, method := range getTestMethods(prototype) {
		var tf ogletest.TestFunction

		// Create an instance to be shared among SetUp and the test function itself.
		var instance reflect.Value = reflect.New(suiteType)

		// SetUp should create a bucket and then initialize the suite object,
		// remembering that the suite implements bucketTestSetUpInterface.
		tf.SetUp = func(*ogletest.TestInfo) {
			bucket := makeBucket()
			instance.Interface().(bucketTestSetUpInterface).SetUpBucketTest(bucket)
		}

		// The test function itself should simply invoke the method.
		methodCopy := method
		tf.Run = func() {
			methodCopy.Func.Call([]reflect.Value{instance})
		}

		// Save the test function.
		ts.TestFunctions = append(ts.TestFunctions, tf)
	}

	// Register the suite.
	ogletest.Register(ts)
}

// Given a function that returns an initialized, empty bucket, register test
// suites that exercise the buckets returned by the function with ogletest.
func RegisterBucketTests(makeBucket func() gcs.Bucket) {
	// A list of empty instances of the test suites we want to register.
	suitePrototypes := []bucketTestSetUpInterface{
		&createTest{},
		&readTest{},
		&deleteTest{},
		&listTest{},
	}

	// Register each.
	for _, suitePrototype := range suitePrototypes {
		registerTestSuite(makeBucket, suitePrototype)
	}
}
<|endoftext|>