<commit_before>package whproxy

import (
	"bufio"
	"io"
	"net/http"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gorilla/websocket"
	"github.com/taskcluster/webhooktunnel/util"
	"github.com/taskcluster/webhooktunnel/wsmux"
)

const (
	monthUnix = 31 * 24 * time.Hour
)

// Config contains the run time parameters for the proxy
type Config struct {
	// Upgrader is a websocket.Upgrader instance which is used to upgrade incoming
	// websocket connections from Clients.
	Upgrader websocket.Upgrader

	// Logger is used to log proxy events. Refer util.Logger.
	Logger util.Logger

	// JWTSecretA and JWTSecretB are used by the proxy to verify JWTs from Clients.
	JWTSecretA []byte
	JWTSecretB []byte
	// Domain where proxy will be hosted
	Domain string
	// set to true if serving with TLS
	TLS bool

	DomainHosted bool
}

// proxy is used to send http and ws requests to workers.
// New proxy can be created by using whproxy.New()
type proxy struct {
	m               sync.RWMutex
	pool            map[string]*wsmux.Session
	upgrader        websocket.Upgrader
	logger          util.Logger
	onSessionRemove func(string)
	jwtSecretA      []byte
	jwtSecretB      []byte
	domain          string
	domainHosted    bool
	tls             bool
}

// regex for parsing requests
var (
	registerRe = regexp.MustCompile("^/register/([a-z0-9_-]+)/?$")
	serveRe    = regexp.MustCompile("^/([a-z0-9_-]+)/(.*)$")
)

// New creates a new proxy instance and wraps it as an http.Handler.
func New(conf Config) (http.Handler, error) {
	return newProxy(conf)
}

// ServeHTTP implements http.Handler so that the proxy may be used as a handler in a Mux or http.Server
func (p *proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	// check for path style
	// eg. domain = "tcproxy.net", req url = "https://tcproxy.net/.../"
	// host may be of the form "host:port"
	// register requests can only be path style
	p.logf("", r.RemoteAddr, "Host=%s Path=%s", r.Host, r.URL.Path)
	if strings.HasPrefix(r.Host, p.domain) {
		// register will be matched first
		if registerRe.MatchString(r.URL.Path) { // matches "/register/(\w+)/?$"
			id := registerRe.FindStringSubmatch(r.URL.Path)[1]
			tokenString := util.ExtractJWT(r.Header.Get("Authorization"))
			p.register(w, r, id, tokenString)
			return
		}

		s := strings.TrimPrefix(r.URL.Path, "/")
		index := strings.Index(s, "/")
		id, path := "", "/"
		if index < 0 {
			id = s
		} else {
			id = s[:index]
			path = s[index:]
		}
		if id == "" {
			http.NotFound(w, r)
			return
		}
		p.serveRequest(w, r, id, path)
		return
	}

	// if address has port, strip port
	host := r.Host
	index := strings.Index(host, ":")
	if index > 0 {
		host = host[:index]
	}
	p.logf("", r.RemoteAddr, "rewritten host=%s", host)
	if strings.HasSuffix(host, "."+p.domain) {
		index := strings.Index(r.Host, ".")
		id := r.Host[:index]
		path := r.URL.Path
		if id == "" {
			http.NotFound(w, r)
			return
		}
		p.serveRequest(w, r, id, path)
		return
	}

	http.NotFound(w, r)
}

func newProxy(conf Config) (*proxy, error) {
	p := &proxy{
		pool:         make(map[string]*wsmux.Session),
		upgrader:     conf.Upgrader,
		logger:       conf.Logger,
		jwtSecretA:   conf.JWTSecretA,
		jwtSecretB:   conf.JWTSecretB,
		domain:       conf.Domain,
		domainHosted: conf.DomainHosted,
		tls:          conf.TLS,
	}

	if len(p.jwtSecretA) == 0 || len(p.jwtSecretB) == 0 {
		panic("whproxy: missing secrets")
	}

	if p.logger == nil {
		p.logger = &util.NilLogger{}
	}

	return p, nil

}

// setSessionRemoveHandler sets a function which is called when a wsmux Session is removed from
// the proxy due to closure or error.
func (p *proxy) setSessionRemoveHandler(h func(string)) {
	p.m.Lock()
	defer p.m.Unlock()
	p.onSessionRemove = h
}

// getWorkerSession returns true if a session with the given id is present
func (p *proxy) getWorkerSession(id string) (*wsmux.Session, bool) {
	p.m.RLock()
	defer p.m.RUnlock()
	s, ok := p.pool[id]
	return s, ok
}

// removeWorker is an idempotent operation which deletes a worker from the proxy's
// worker pool
func (p *proxy) removeWorker(id string) {
	p.m.Lock()
	defer p.m.Unlock()
	delete(p.pool, id)
	p.logf(id, "", "session removed")
}

// register is used to connect a worker to the proxy so that it can start serving API endpoints.
// The request must contain the worker ID in the url.
// The request is validated by the proxy and the http connection is upgraded to websocket.
func (p *proxy) register(w http.ResponseWriter, r *http.Request, id, tokenString string) {

	p.logf(id, r.RemoteAddr, "requesting client registration")
	if tokenString == "" {
		// No jwt. Connection not authorized
		p.logerrorf(id, r.RemoteAddr, "could not retreive auth token")
		http.Error(w, http.StatusText(400), 400)
		return
	}

	if !websocket.IsWebSocketUpgrade(r) {
		p.logerrorf(id, r.RemoteAddr, "request must be websocket upgrade")
		http.NotFound(w, r)
		return
	}

	// validation does not require lock
	if err := p.validateJWT(id, tokenString); err != nil {
		p.logerrorf(id, r.RemoteAddr, "unable to validate token: %v", err)
		http.Error(w, http.StatusText(400), 400)
		return
	}

	// we should lock while upgrading
	// otherwise there could be a possible race condition
	p.m.Lock()
	defer p.m.Unlock()

	// remove old session and allow new connection
	session := p.pool[id]

	// remove the close callback so that functions are not accidentally called
	if session != nil {
		p.logf(id, r.RemoteAddr, "removed previous session")
		session.SetCloseCallback(nil)
	}
	// unlock and close old session while upgrading
	if session != nil {
		_ = session.Close()
	}

	delete(p.pool, id)

	header := make(http.Header)

	urlScheme := "http://"
	if p.tls {
		urlScheme = "https://"
	}

	url := urlScheme + p.domain + "/" + id
	if p.domainHosted {
		url = urlScheme + id + "." + p.domain
	}
	header.Set("x-webhooktunnel-client-url", url)
	p.logf(id, r.RemoteAddr, "sending url= %s", url)
	conn, err := p.upgrader.Upgrade(w, r, header)
	if err != nil {
		p.logger.Print(err)
		return
	}

	// generate config
	conf := wsmux.Config{
		StreamBufferSize: 4 * 1024,
		CloseCallback: func() {
			p.removeWorker(id)
			if p.onSessionRemove != nil {
				p.onSessionRemove(id)
			}
		},
		// Log: p.logger,
	}

	p.pool[id] = wsmux.Server(conn, conf)
	p.logf(id, r.RemoteAddr, "added new session for worker")
}

// serveRequest serves worker endpoints to viewers
func (p *proxy) serveRequest(w http.ResponseWriter, r *http.Request, id string, path string) {
	// log new request arrival
	p.logf(id, r.RemoteAddr, "request: host=%s path=%s", r.Host, path)

	session, ok := p.getWorkerSession(id)

	// 404 if worker is not registered on this proxy
	if !ok {
		// DHT code will be added here
		p.logerrorf(id, r.RemoteAddr, "could not find requested worker")
		http.Error(w, http.StatusText(404), 404)
		return
	}

	// set original path as header
	r.Header.Set("x-webhooktunnel-original-path", r.URL.Path)

	// check for a websocket request
	if websocket.IsWebSocketUpgrade(r) {
		_ = p.websocketProxy(w, r, session, id)
		return
	}

	// Open a stream to the worker session
	r.URL.Path = path
	p.logf(id, r.RemoteAddr, "attempting to open new stream")
	reqStream, streamID, err := session.Open()
	if err != nil {
		p.logerrorf(id, r.RemoteAddr, "could not open stream: path=%s", path)
		http.Error(w, http.StatusText(500), 500)
		return
	}
	p.logf(id, r.RemoteAddr, "opened new stream: ID=%d", streamID)

	// rewrite path for worker and write request
	err = r.Write(reqStream)
	if err != nil {
		p.logerrorf(id, r.RemoteAddr, "could not write request: path=%s", path)
		http.Error(w, http.StatusText(500), 500)
		return
	}

	// read response from worker
	bufReader := bufio.NewReader(reqStream)
	resp, err := http.ReadResponse(bufReader, r)
	if err != nil {
		p.logerrorf(id, r.RemoteAddr, "could not read response: path=%s", path)
		http.Error(w, http.StatusText(500), 500)
		return
	}

	// manually proxy response
	// clear responseWriter headers and write response headers instead
	for k := range w.Header() {
		w.Header().Del(k)
	}
	for k, v := range resp.Header {
		w.Header()[k] = v
	}

	// dump headers
	w.WriteHeader(resp.StatusCode)
	if resp.Body == nil {
		return
	}

	// stream body to viewer and close wsmux stream
	defer func() {
		_ = reqStream.Close()
	}()

	flusher, ok := w.(http.Flusher)
	// flusher may not be implemented by a ResponseWriter wrapper
	// simple copy
	if !ok {
		n, err := io.Copy(w, resp.Body)
		p.logf(id, r.RemoteAddr, "data transfered over request: %d bytes, error: %v", n, err)
		// log here
		return
	}

	p.logf(id, r.RemoteAddr, "streaming http")
	wf := &threadSafeWriteFlusher{w: w, f: flusher}
	n, err := copyAndFlush(wf, resp.Body, 100*time.Millisecond)
	p.logf(id, r.RemoteAddr, "data transfered over request: %d bytes, error: %v", n, err)
}

// validate jwt
// jwt signing and verification algorithm must be HMAC
func (p *proxy) validateJWT(id string, tokenString string) error {
	// parse jwt token
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, ErrUnexpectedSigningMethod
		}
		return p.jwtSecretA, nil
	})

	if err != nil {
		// log first error
		p.logerrorf(id, "", "%v: trying with second secret", err)

		token, err = jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, ErrUnexpectedSigningMethod
			}
			return p.jwtSecretB, nil
		})
	}

	if err != nil {
		p.logerrorf(id, "", "%v: auth failed", err)
		return ErrAuthFailed
	}

	// check claims
	now := time.Now().Unix()
	claims, ok := token.Claims.(jwt.MapClaims)

	if !ok {
		p.logerrorf(id, "", "%v: could not parse claims", err)
		return ErrTokenNotValid
	}
	p.logf(id, "", "claims: %v", claims)

	if !claims.VerifyExpiresAt(now, true) {
		p.logerrorf(id, "", "%v", err)
		return ErrAuthFailed
	}
	if !claims.VerifyIssuedAt(now, true) {
		p.logerrorf(id, "", "%v", err)
		return ErrAuthFailed
	}
	if !claims.VerifyNotBefore(now, true) {
		p.logerrorf(id, "", "%v", err)
		return ErrAuthFailed
	}
	if claims["tid"] != id {
		p.logerrorf(id, "", "%v", err)
		return ErrAuthFailed
	}

	if claims["exp"].(float64)-claims["nbf"].(float64) > float64(monthUnix) {
		p.logerrorf(id, "", "jwt should not be valid for more than 31 days")
		return ErrAuthFailed
	}

	return nil
}

// proxy logging utilities
const (
	fmtString      = "[PROXY] INFO: id=%s remote_ip=%s "
	fmtErrorString = "[PROXY] ERROR: id=%s remote_ip=%s "
)

// NOTE: cannot use logrus methods
func (p *proxy) logf(id string, remoteAddr string, format string, v ...interface{}) {
	args := []interface{}{id, remoteAddr}
	args = append(args, v...)
	p.logger.Printf(fmtString+format, args...)
}

func (p *proxy) logerrorf(id string, remoteAddr string, format string, v ...interface{}) {
	args := []interface{}{id, remoteAddr}
	args = append(args, v...)
	p.logger.Printf(fmtErrorString+format, args...)
}
<commit_msg>do not verify iat in jwt<commit_after>package whproxy

import (
	"bufio"
	"io"
	"net/http"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gorilla/websocket"
	"github.com/taskcluster/webhooktunnel/util"
	"github.com/taskcluster/webhooktunnel/wsmux"
)

const (
	monthUnix = 31 * 24 * time.Hour
)

// Config contains the run time parameters for the proxy
type Config struct {
	// Upgrader is a websocket.Upgrader instance which is used to upgrade incoming
	// websocket connections from Clients.
	Upgrader websocket.Upgrader

	// Logger is used to log proxy events. Refer util.Logger.
	Logger util.Logger

	// JWTSecretA and JWTSecretB are used by the proxy to verify JWTs from Clients.
	JWTSecretA []byte
	JWTSecretB []byte
	// Domain where proxy will be hosted
	Domain string
	// set to true if serving with TLS
	TLS bool

	DomainHosted bool
}

// proxy is used to send http and ws requests to workers.
// New proxy can be created by using whproxy.New()
type proxy struct {
	m               sync.RWMutex
	pool            map[string]*wsmux.Session
	upgrader        websocket.Upgrader
	logger          util.Logger
	onSessionRemove func(string)
	jwtSecretA      []byte
	jwtSecretB      []byte
	domain          string
	domainHosted    bool
	tls             bool
}

// regex for parsing requests
var (
	registerRe = regexp.MustCompile("^/register/([a-z0-9_-]+)/?$")
	serveRe    = regexp.MustCompile("^/([a-z0-9_-]+)/(.*)$")
)

// New creates a new proxy instance and wraps it as an http.Handler.
func New(conf Config) (http.Handler, error) {
	return newProxy(conf)
}

// ServeHTTP implements http.Handler so that the proxy may be used as a handler in a Mux or http.Server
func (p *proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	// check for path style
	// eg. domain = "tcproxy.net", req url = "https://tcproxy.net/.../"
	// host may be of the form "host:port"
	// register requests can only be path style
	p.logf("", r.RemoteAddr, "Host=%s Path=%s", r.Host, r.URL.Path)
	if strings.HasPrefix(r.Host, p.domain) {
		// register will be matched first
		if registerRe.MatchString(r.URL.Path) { // matches "/register/(\w+)/?$"
			id := registerRe.FindStringSubmatch(r.URL.Path)[1]
			tokenString := util.ExtractJWT(r.Header.Get("Authorization"))
			p.register(w, r, id, tokenString)
			return
		}

		s := strings.TrimPrefix(r.URL.Path, "/")
		index := strings.Index(s, "/")
		id, path := "", "/"
		if index < 0 {
			id = s
		} else {
			id = s[:index]
			path = s[index:]
		}
		if id == "" {
			http.NotFound(w, r)
			return
		}
		p.serveRequest(w, r, id, path)
		return
	}

	// if address has port, strip port
	host := r.Host
	index := strings.Index(host, ":")
	if index > 0 {
		host = host[:index]
	}
	p.logf("", r.RemoteAddr, "rewritten host=%s", host)
	if strings.HasSuffix(host, "."+p.domain) {
		index := strings.Index(r.Host, ".")
		id := r.Host[:index]
		path := r.URL.Path
		if id == "" {
			http.NotFound(w, r)
			return
		}
		p.serveRequest(w, r, id, path)
		return
	}

	http.NotFound(w, r)
}

func newProxy(conf Config) (*proxy, error) {
	p := &proxy{
		pool:         make(map[string]*wsmux.Session),
		upgrader:     conf.Upgrader,
		logger:       conf.Logger,
		jwtSecretA:   conf.JWTSecretA,
		jwtSecretB:   conf.JWTSecretB,
		domain:       conf.Domain,
		domainHosted: conf.DomainHosted,
		tls:          conf.TLS,
	}

	if len(p.jwtSecretA) == 0 || len(p.jwtSecretB) == 0 {
		panic("whproxy: missing secrets")
	}

	if p.logger == nil {
		p.logger = &util.NilLogger{}
	}

	return p, nil

}

// setSessionRemoveHandler sets a function which is called when a wsmux Session is removed from
// the proxy due to closure or error.
func (p *proxy) setSessionRemoveHandler(h func(string)) {
	p.m.Lock()
	defer p.m.Unlock()
	p.onSessionRemove = h
}

// getWorkerSession returns true if a session with the given id is present
func (p *proxy) getWorkerSession(id string) (*wsmux.Session, bool) {
	p.m.RLock()
	defer p.m.RUnlock()
	s, ok := p.pool[id]
	return s, ok
}

// removeWorker is an idempotent operation which deletes a worker from the proxy's
// worker pool
func (p *proxy) removeWorker(id string) {
	p.m.Lock()
	defer p.m.Unlock()
	delete(p.pool, id)
	p.logf(id, "", "session removed")
}

// register is used to connect a worker to the proxy so that it can start serving API endpoints.
// The request must contain the worker ID in the url.
// The request is validated by the proxy and the http connection is upgraded to websocket.
func (p *proxy) register(w http.ResponseWriter, r *http.Request, id, tokenString string) {

	p.logf(id, r.RemoteAddr, "requesting client registration")
	if tokenString == "" {
		// No jwt. Connection not authorized
		p.logerrorf(id, r.RemoteAddr, "could not retreive auth token")
		http.Error(w, http.StatusText(400), 400)
		return
	}

	if !websocket.IsWebSocketUpgrade(r) {
		p.logerrorf(id, r.RemoteAddr, "request must be websocket upgrade")
		http.NotFound(w, r)
		return
	}

	// validation does not require lock
	if err := p.validateJWT(id, tokenString); err != nil {
		p.logerrorf(id, r.RemoteAddr, "unable to validate token: %v", err)
		http.Error(w, http.StatusText(400), 400)
		return
	}

	// we should lock while upgrading
	// otherwise there could be a possible race condition
	p.m.Lock()
	defer p.m.Unlock()

	// remove old session and allow new connection
	session := p.pool[id]

	// remove the close callback so that functions are not accidentally called
	if session != nil {
		p.logf(id, r.RemoteAddr, "removed previous session")
		session.SetCloseCallback(nil)
	}
	// unlock and close old session while upgrading
	if session != nil {
		_ = session.Close()
	}

	delete(p.pool, id)

	header := make(http.Header)

	urlScheme := "http://"
	if p.tls {
		urlScheme = "https://"
	}

	url := urlScheme + p.domain + "/" + id
	if p.domainHosted {
		url = urlScheme + id + "." + p.domain
	}
	header.Set("x-webhooktunnel-client-url", url)
	p.logf(id, r.RemoteAddr, "sending url= %s", url)
	conn, err := p.upgrader.Upgrade(w, r, header)
	if err != nil {
		p.logger.Print(err)
		return
	}

	// generate config
	conf := wsmux.Config{
		StreamBufferSize: 4 * 1024,
		CloseCallback: func() {
			p.removeWorker(id)
			if p.onSessionRemove != nil {
				p.onSessionRemove(id)
			}
		},
		// Log: p.logger,
	}

	p.pool[id] = wsmux.Server(conn, conf)
	p.logf(id, r.RemoteAddr, "added new session for worker")
}

// serveRequest serves worker endpoints to viewers
func (p *proxy) serveRequest(w http.ResponseWriter, r *http.Request, id string, path string) {
	// log new request arrival
	p.logf(id, r.RemoteAddr, "request: host=%s path=%s", r.Host, path)

	session, ok := p.getWorkerSession(id)

	// 404 if worker is not registered on this proxy
	if !ok {
		// DHT code will be added here
		p.logerrorf(id, r.RemoteAddr, "could not find requested worker")
		http.Error(w, http.StatusText(404), 404)
		return
	}

	// set original path as header
	r.Header.Set("x-webhooktunnel-original-path", r.URL.Path)

	// check for a websocket request
	if websocket.IsWebSocketUpgrade(r) {
		_ = p.websocketProxy(w, r, session, id)
		return
	}

	// Open a stream to the worker session
	r.URL.Path = path
	p.logf(id, r.RemoteAddr, "attempting to open new stream")
	reqStream, streamID, err := session.Open()
	if err != nil {
		p.logerrorf(id, r.RemoteAddr, "could not open stream: path=%s", path)
		http.Error(w, http.StatusText(500), 500)
		return
	}
	p.logf(id, r.RemoteAddr, "opened new stream: ID=%d", streamID)

	// rewrite path for worker and write request
	err = r.Write(reqStream)
	if err != nil {
		p.logerrorf(id, r.RemoteAddr, "could not write request: path=%s", path)
		http.Error(w, http.StatusText(500), 500)
		return
	}

	// read response from worker
	bufReader := bufio.NewReader(reqStream)
	resp, err := http.ReadResponse(bufReader, r)
	if err != nil {
		p.logerrorf(id, r.RemoteAddr, "could not read response: path=%s", path)
		http.Error(w, http.StatusText(500), 500)
		return
	}

	// manually proxy response
	// clear responseWriter headers and write response headers instead
	for k := range w.Header() {
		w.Header().Del(k)
	}
	for k, v := range resp.Header {
		w.Header()[k] = v
	}

	// dump headers
	w.WriteHeader(resp.StatusCode)
	if resp.Body == nil {
		return
	}

	// stream body to viewer and close wsmux stream
	defer func() {
		_ = reqStream.Close()
	}()

	flusher, ok := w.(http.Flusher)
	// flusher may not be implemented by a ResponseWriter wrapper
	// simple copy
	if !ok {
		n, err := io.Copy(w, resp.Body)
		p.logf(id, r.RemoteAddr, "data transfered over request: %d bytes, error: %v", n, err)
		// log here
		return
	}

	p.logf(id, r.RemoteAddr, "streaming http")
	wf := &threadSafeWriteFlusher{w: w, f: flusher}
	n, err := copyAndFlush(wf, resp.Body, 100*time.Millisecond)
	p.logf(id, r.RemoteAddr, "data transfered over request: %d bytes, error: %v", n, err)
}

// validate jwt
// jwt signing and verification algorithm must be HMAC
func (p *proxy) validateJWT(id string, tokenString string) error {
	// parse jwt token
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, ErrUnexpectedSigningMethod
		}
		return p.jwtSecretA, nil
	})

	if err != nil {
		// log first error
		p.logerrorf(id, "", "%v: trying with second secret", err)

		token, err = jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, ErrUnexpectedSigningMethod
			}
			return p.jwtSecretB, nil
		})
	}

	if err != nil {
		p.logerrorf(id, "", "%v: auth failed", err)
		return ErrAuthFailed
	}

	// check claims
	now := time.Now().Unix()
	claims, ok := token.Claims.(jwt.MapClaims)

	if !ok {
		p.logerrorf(id, "", "%v: could not parse claims", err)
		return ErrTokenNotValid
	}
	p.logf(id, "", "claims: %v", claims)

	if !claims.VerifyExpiresAt(now, true) {
		p.logerrorf(id, "", "%v", err)
		return ErrAuthFailed
	}
	if !claims.VerifyNotBefore(now, true) {
		p.logerrorf(id, "", "%v", err)
		return ErrAuthFailed
	}
	if claims["tid"] != id {
		p.logerrorf(id, "", "%v", err)
		return ErrAuthFailed
	}

	if claims["exp"].(float64)-claims["nbf"].(float64) > float64(monthUnix) {
		p.logerrorf(id, "", "jwt should not be valid for more than 31 days")
		return ErrAuthFailed
	}

	return nil
}

// proxy logging utilities
const (
	fmtString      = "[PROXY] INFO: id=%s remote_ip=%s "
	fmtErrorString = "[PROXY] ERROR: id=%s remote_ip=%s "
)

// NOTE: cannot use logrus methods
func (p *proxy) logf(id string, remoteAddr string, format string, v ...interface{}) {
	args := []interface{}{id, remoteAddr}
	args = append(args, v...)
	p.logger.Printf(fmtString+format, args...)
}

func (p *proxy) logerrorf(id string, remoteAddr string, format string, v ...interface{}) {
	args := []interface{}{id, remoteAddr}
	args = append(args, v...)
	p.logger.Printf(fmtErrorString+format, args...)
}
<|endoftext|>


<commit_before>// Copyright 2010 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 main

import (
	"bytes"
	"exec"
	"flag"
	"http"
	"io"
	"io/ioutil"
	"log"
	"os"
	"runtime"
	"strconv"
	"template"
)

var (
	httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput = flag.Bool("html", false, "render program output as HTML")
)

var (
	// a source of numbers, for naming temporary files
	uniq = make(chan int)
	// the architecture-identifying character of the tool chain, 5, 6, or 8
	archChar string
)

func main() {
	flag.Parse()

	// set archChar
	switch runtime.GOARCH {
	case "arm":
		archChar = "5"
	case "amd64":
		archChar = "6"
	case "386":
		archChar = "8"
	default:
		log.Fatalln("unrecognized GOARCH:", runtime.GOARCH)
	}

	// source of unique numbers
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()

	http.HandleFunc("/", FrontPage)
	http.HandleFunc("/compile", Compile)
	log.Fatal(http.ListenAndServe(*httpListen, nil))
}

// FrontPage is an HTTP handler that renders the goplay interface.
// If a filename is supplied in the path component of the URI,
// its contents will be put in the interface's text area.
// Otherwise, the default "hello, world" program is displayed.
func FrontPage(w http.ResponseWriter, req *http.Request) {
	data, err := ioutil.ReadFile(req.URL.Path[1:])
	if err != nil {
		data = helloWorld
	}
	frontPage.Execute(w, data)
}

// Compile is an HTTP handler that reads Go source code from the request,
// compiles and links the code (returning any errors), runs the program,
// and sends the program's output as the HTTP response.
func Compile(w http.ResponseWriter, req *http.Request) {
	// x is the base name for .go, .6, executable files
	x := os.TempDir() + "/compile" + strconv.Itoa(<-uniq)
	src := x + ".go"
	obj := x + "." + archChar
	bin := x
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	// write request Body to x.go
	f, err := os.Create(src)
	if err != nil {
		error(w, nil, err)
		return
	}
	defer os.Remove(src)
	defer f.Close()
	_, err = io.Copy(f, req.Body)
	if err != nil {
		error(w, nil, err)
		return
	}
	f.Close()

	// build x.go, creating x.6
	out, err := run(archChar+"g", "-o", obj, src)
	defer os.Remove(obj)
	if err != nil {
		error(w, out, err)
		return
	}

	// link x.6, creating x (the program binary)
	out, err = run(archChar+"l", "-o", bin, obj)
	defer os.Remove(bin)
	if err != nil {
		error(w, out, err)
		return
	}

	// run x
	out, err = run(bin)
	if err != nil {
		error(w, out, err)
	}

	// write the output of x as the http response
	if *htmlOutput {
		w.Write(out)
	} else {
		output.Execute(w, out)
	}
}

// error writes compile, link, or runtime errors to the HTTP connection.
// The JavaScript interface uses the 404 status code to identify the error.
func error(w http.ResponseWriter, out []byte, err os.Error) {
	w.WriteHeader(404)
	if out != nil {
		output.Execute(w, out)
	} else {
		output.Execute(w, err.String())
	}
}

// run executes the specified command and returns its output and an error.
func run(cmd ...string) ([]byte, os.Error) {
	// find the specified binary
	bin, err := exec.LookPath(cmd[0])
	if err != nil {
		// report binary as well as the error
		return nil, os.NewError(cmd[0] + ": " + err.String())
	}

	// run the binary and read its combined stdout and stderr into a buffer
	p, err := exec.Run(bin, cmd, os.Environ(), "", exec.DevNull, exec.Pipe, exec.MergeWithStdout)
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	io.Copy(&buf, p.Stdout)
	w, err := p.Wait(0)
	p.Close()
	if err != nil {
		return nil, err
	}

	// set the error return value if the program had a non-zero exit status
	if !w.Exited() || w.ExitStatus() != 0 {
		err = os.ErrorString("running " + cmd[0] + ": " + w.String())
	}

	return buf.Bytes(), err
}

var frontPage, output *template.Template // HTML templates

func init() {
	frontPage = template.New(nil)
	frontPage.SetDelims("«", "»")
	if err := frontPage.Parse(frontPageText); err != nil {
		panic(err)
	}
	output = template.MustParse(outputText, nil)
}

var outputText = `<pre>{@|html}</pre>`

var frontPageText = `<!doctype html>

<textarea autofocus="true" id="edit" spellcheck="false" onkeydown="keyHandler();" onkeyup="autocompile();">«@|html»</textarea>

(Shift-Enter to compile and run.)
<input type="checkbox" id="autocompile" value="checked" /> Compile and run after each keystroke

`

var helloWorld = []byte(`package main

import "fmt"

func main() {
	fmt.Println("hello, world")
}
`)
<commit_msg>misc/goplay: fix Tab and Shift+Enter in Firefox.<commit_after>// Copyright 2010 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 main

import (
	"bytes"
	"exec"
	"flag"
	"http"
	"io"
	"io/ioutil"
	"log"
	"os"
	"runtime"
	"strconv"
	"template"
)

var (
	httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput = flag.Bool("html", false, "render program output as HTML")
)

var (
	// a source of numbers, for naming temporary files
	uniq = make(chan int)
	// the architecture-identifying character of the tool chain, 5, 6, or 8
	archChar string
)

func main() {
	flag.Parse()

	// set archChar
	switch runtime.GOARCH {
	case "arm":
		archChar = "5"
	case "amd64":
		archChar = "6"
	case "386":
		archChar = "8"
	default:
		log.Fatalln("unrecognized GOARCH:", runtime.GOARCH)
	}

	// source of unique numbers
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()

	http.HandleFunc("/", FrontPage)
	http.HandleFunc("/compile", Compile)
	log.Fatal(http.ListenAndServe(*httpListen, nil))
}

// FrontPage is an HTTP handler that renders the goplay interface.
// If a filename is supplied in the path component of the URI,
// its contents will be put in the interface's text area.
// Otherwise, the default "hello, world" program is displayed.
func FrontPage(w http.ResponseWriter, req *http.Request) {
	data, err := ioutil.ReadFile(req.URL.Path[1:])
	if err != nil {
		data = helloWorld
	}
	frontPage.Execute(w, data)
}

// Compile is an HTTP handler that reads Go source code from the request,
// compiles and links the code (returning any errors), runs the program,
// and sends the program's output as the HTTP response.
func Compile(w http.ResponseWriter, req *http.Request) {
	// x is the base name for .go, .6, executable files
	x := os.TempDir() + "/compile" + strconv.Itoa(<-uniq)
	src := x + ".go"
	obj := x + "." + archChar
	bin := x
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	// write request Body to x.go
	f, err := os.Create(src)
	if err != nil {
		error(w, nil, err)
		return
	}
	defer os.Remove(src)
	defer f.Close()
	_, err = io.Copy(f, req.Body)
	if err != nil {
		error(w, nil, err)
		return
	}
	f.Close()

	// build x.go, creating x.6
	out, err := run(archChar+"g", "-o", obj, src)
	defer os.Remove(obj)
	if err != nil {
		error(w, out, err)
		return
	}

	// link x.6, creating x (the program binary)
	out, err = run(archChar+"l", "-o", bin, obj)
	defer os.Remove(bin)
	if err != nil {
		error(w, out, err)
		return
	}

	// run x
	out, err = run(bin)
	if err != nil {
		error(w, out, err)
	}

	// write the output of x as the http response
	if *htmlOutput {
		w.Write(out)
	} else {
		output.Execute(w, out)
	}
}

// error writes compile, link, or runtime errors to the HTTP connection.
// The JavaScript interface uses the 404 status code to identify the error.
func error(w http.ResponseWriter, out []byte, err os.Error) {
	w.WriteHeader(404)
	if out != nil {
		output.Execute(w, out)
	} else {
		output.Execute(w, err.String())
	}
}

// run executes the specified command and returns its output and an error.
func run(cmd ...string) ([]byte, os.Error) {
	// find the specified binary
	bin, err := exec.LookPath(cmd[0])
	if err != nil {
		// report binary as well as the error
		return nil, os.NewError(cmd[0] + ": " + err.String())
	}

	// run the binary and read its combined stdout and stderr into a buffer
	p, err := exec.Run(bin, cmd, os.Environ(), "", exec.DevNull, exec.Pipe, exec.MergeWithStdout)
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	io.Copy(&buf, p.Stdout)
	w, err := p.Wait(0)
	p.Close()
	if err != nil {
		return nil, err
	}

	// set the error return value if the program had a non-zero exit status
	if !w.Exited() || w.ExitStatus() != 0 {
		err = os.ErrorString("running " + cmd[0] + ": " + w.String())
	}

	return buf.Bytes(), err
}

var frontPage, output *template.Template // HTML templates

func init() {
	frontPage = template.New(nil)
	frontPage.SetDelims("«", "»")
	if err := frontPage.Parse(frontPageText); err != nil {
		panic(err)
	}
	output = template.MustParse(outputText, nil)
}

var outputText = `<pre>{@|html}</pre>`

var frontPageText = `<!doctype html>

<textarea autofocus="true" id="edit" spellcheck="false" onkeydown="keyHandler(event);" onkeyup="autocompile();">«@|html»</textarea>

(Shift-Enter to compile and run.)
<input type="checkbox" id="autocompile" value="checked" /> Compile and run after each keystroke

`

var helloWorld = []byte(`package main

import "fmt"

func main() {
	fmt.Println("hello, world")
}
`)
<|endoftext|>


<commit_before>package internal

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/build"
	"go/importer"
	"path/filepath"
	"runtime"
	// "go/internal/gcimporter"

	"go/types"
	"os"
	"os/exec"
	"strings"
)

func GetTypeInfo(pkgCtx *PackageContext, pkgDir, importPath, tempGoSrcDir string) (*types.Info, error) {
	buildPackage, err := build.Import(importPath, tempGoSrcDir, build.AllowBinary)
	if err != nil {
		return nil, err
	}

	deps, err := FindDeps(buildPackage)
	if err != nil {
		return nil, err
	}

	for i, dep := range deps {
		// NOTE: rewrite $dep to $importpath/vendor/$dep if $dep is found in vendor dir
		if pkgCtx.HasVendor {
			if _, err := os.Stat(filepath.Join(pkgCtx.Vendor, dep)); err == nil {
				pkgToVendor, err := filepath.Rel(pkgDir, pkgCtx.Vendor)
				if err != nil {
					return nil, err
				}
				dep = filepath.Join(pkgToVendor, dep)
				if !strings.HasPrefix(dep, ".") {
					dep = "./" + dep
				}
			}
		}

		deps[i] = dep
	}

	if IsBuildableFileSet(pkgCtx.NormalizedFset) {
		deps = append(deps, ".")
	}

	if len(deps) > 0 {
		install := exec.Command("go", "install")
		install.Dir = pkgDir
		install.Stdout = os.Stdout
		b := []byte{}
		buf := bytes.NewBuffer(b)
		install.Stderr = buf
		if Verbose {
			install.Args = append(install.Args, "-v")
		}
		install.Args = append(install.Args, deps...)
		if err := install.Run(); err != nil {
			return nil, fmt.Errorf("[ERROR] go install %s\n\n%s", strings.Join(deps, " "), buf.String())
		}
	}

	if pkgCtx.HasVendor {
		// NOTE: move pkg/$importpath/vendor/$v-importpath to pkg/$v-importpath
		pd := filepath.Join(os.Getenv("GOPATH"), "pkg", runtime.GOOS+"_"+runtime.GOARCH)
		err := filepath.Walk(pd, func(path string, finfo os.FileInfo, err error) error {
			if path == pd {
				return nil
			}

			vimportpath, found := RetrieveImportpathFromVendorDir(path)
			if !found {
				return nil
			}

			outpath := filepath.Join(pd, vimportpath)
			if finfo.IsDir() {
				return os.MkdirAll(outpath, finfo.Mode())
			}
			out, err := os.OpenFile(outpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, finfo.Mode())
			if err != nil {
				return err
			}
			defer out.Close()
			return CopyFile(path, out)
		})
		if err != nil {
			return nil, err
		}
	}

	// Assume types from ast.Node
	typesConfig := types.Config{}
	typesConfig.Importer = importer.Default()
	pkg := types.NewPackage(importPath, "")
	info := &types.Info{
		Types:      map[ast.Expr]types.TypeAndValue{},
		Defs:       map[*ast.Ident]types.Object{},
		Uses:       map[*ast.Ident]types.Object{},
		Implicits:  map[ast.Node]types.Object{},
		Selections: map[*ast.SelectorExpr]*types.Selection{},
		Scopes:     map[ast.Node]*types.Scope{},
		InitOrder:  []*types.Initializer{},
	}

	// WORKARROUND(xtest): if the tests is xtest like a `{pkg}_test`, check types only in `{pkg}_test`
	// NOTE: if your check types `package {pkg}_test` and `package {pkg}`, you'll get `package {pkg}_test; expected {pkg}`
	isXtest := false
	xtests := []*ast.File{}
	for _, f := range pkgCtx.NormalizedFiles {
		if strings.HasSuffix(f.Name.Name, "_test") {
			xtests = append(xtests, f)
		}
	}
	isXtest = len(xtests) > 0 && len(xtests) != len(pkgCtx.NormalizedFiles)

	if isXtest {
		err = types.NewChecker(&typesConfig, pkgCtx.NormalizedFset, pkg, info).Files(xtests)
	} else {
		err = types.NewChecker(&typesConfig, pkgCtx.NormalizedFset, pkg, info).Files(pkgCtx.NormalizedFiles)
	}
	if err != nil {
		return nil, err
	}

	return info, nil
}

func DeterminantExprOfIsTypeConversion(e ast.Expr) ast.Expr {
	switch e.(type) {
	case *ast.ParenExpr:
		return DeterminantExprOfIsTypeConversion(e.(*ast.ParenExpr).X)
	case *ast.StarExpr:
		return DeterminantExprOfIsTypeConversion(e.(*ast.StarExpr).X)
	case *ast.CallExpr:
		return DeterminantExprOfIsTypeConversion(e.(*ast.CallExpr).Fun)
	case *ast.SelectorExpr:
		return e.(*ast.SelectorExpr).Sel
	default:
		return e
	}
}

func IsTypeConversion(info *types.Info, e *ast.CallExpr) bool {
	if info == nil {
		return false
	}

	funcOrType := DeterminantExprOfIsTypeConversion(e)

	switch funcOrType.(type) {
	case *ast.ChanType, *ast.FuncType, *ast.MapType, *ast.ArrayType, *ast.StructType, *ast.InterfaceType:
		return true
	case *ast.Ident:
		id := funcOrType.(*ast.Ident)

		if t, ok := info.Types[id]; ok {
			return t.IsType()
		}

		if o := info.ObjectOf(id); o != nil {
			switch o.(type) {
			case *types.TypeName:
				return true
			default:
				return false
			}
		}
	}

	return false
}
<commit_msg>chore(translator/internal): fix typo in comment<commit_after>package internal

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/build"
	"go/importer"
	"path/filepath"
	"runtime"
	// "go/internal/gcimporter"

	"go/types"
	"os"
	"os/exec"
	"strings"
)

func GetTypeInfo(pkgCtx *PackageContext, pkgDir, importPath, tempGoSrcDir string) (*types.Info, error) {
	buildPackage, err := build.Import(importPath, tempGoSrcDir, build.AllowBinary)
	if err != nil {
		return nil, err
	}

	deps, err := FindDeps(buildPackage)
	if err != nil {
		return nil, err
	}

	for i, dep := range deps {
		// NOTE: rewrite $dep to $importpath/vendor/$dep if $dep is found in vendor dir
		if pkgCtx.HasVendor {
			if _, err := os.Stat(filepath.Join(pkgCtx.Vendor, dep)); err == nil {
				pkgToVendor, err := filepath.Rel(pkgDir, pkgCtx.Vendor)
				if err != nil {
					return nil, err
				}
				dep = filepath.Join(pkgToVendor, dep)
				if !strings.HasPrefix(dep, ".") {
					dep = "./" + dep
				}
			}
		}

		deps[i] = dep
	}

	if IsBuildableFileSet(pkgCtx.NormalizedFset) {
		deps = append(deps, ".")
	}

	if len(deps) > 0 {
		install := exec.Command("go", "install")
		install.Dir = pkgDir
		install.Stdout = os.Stdout
		b := []byte{}
		buf := bytes.NewBuffer(b)
		install.Stderr = buf
		if Verbose {
			install.Args = append(install.Args, "-v")
		}
		install.Args = append(install.Args, deps...)
		if err := install.Run(); err != nil {
			return nil, fmt.Errorf("[ERROR] go install %s\n\n%s", strings.Join(deps, " "), buf.String())
		}
	}

	if pkgCtx.HasVendor {
		// NOTE: move pkg/$importpath/vendor/$v-importpath to pkg/$v-importpath
		pd := filepath.Join(os.Getenv("GOPATH"), "pkg", runtime.GOOS+"_"+runtime.GOARCH)
		err := filepath.Walk(pd, func(path string, finfo os.FileInfo, err error) error {
			if path == pd {
				return nil
			}

			vimportpath, found := RetrieveImportpathFromVendorDir(path)
			if !found {
				return nil
			}

			outpath := filepath.Join(pd, vimportpath)
			if finfo.IsDir() {
				return os.MkdirAll(outpath, finfo.Mode())
			}
			out, err := os.OpenFile(outpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, finfo.Mode())
			if err != nil {
				return err
			}
			defer out.Close()
			return CopyFile(path, out)
		})
		if err != nil {
			return nil, err
		}
	}

	// Assume types from ast.Node
	typesConfig := types.Config{}
	typesConfig.Importer = importer.Default()
	pkg := types.NewPackage(importPath, "")
	info := &types.Info{
		Types:      map[ast.Expr]types.TypeAndValue{},
		Defs:       map[*ast.Ident]types.Object{},
		Uses:       map[*ast.Ident]types.Object{},
		Implicits:  map[ast.Node]types.Object{},
		Selections: map[*ast.SelectorExpr]*types.Selection{},
		Scopes:     map[ast.Node]*types.Scope{},
		InitOrder:  []*types.Initializer{},
	}

	// WORKARROUND(xtest): if the tests is xtest like `{pkg}_test`, check types only in `{pkg}_test`
	// NOTE: if you check types both `package {pkg}_test` and `package {pkg}`, you'll get `package {pkg}_test; expected {pkg}`
	isXtest := false
	xtests := []*ast.File{}
	for _, f := range pkgCtx.NormalizedFiles {
		if strings.HasSuffix(f.Name.Name, "_test") {
			xtests = append(xtests, f)
		}
	}
	isXtest = len(xtests) > 0 && len(xtests) != len(pkgCtx.NormalizedFiles)

	if isXtest {
		err = types.NewChecker(&typesConfig, pkgCtx.NormalizedFset, pkg, info).Files(xtests)
	} else {
		err = types.NewChecker(&typesConfig, pkgCtx.NormalizedFset, pkg, info).Files(pkgCtx.NormalizedFiles)
	}
	if err != nil {
		return nil, err
	}

	return info, nil
}

func DeterminantExprOfIsTypeConversion(e ast.Expr) ast.Expr {
	switch e.(type) {
	case *ast.ParenExpr:
		return DeterminantExprOfIsTypeConversion(e.(*ast.ParenExpr).X)
	case *ast.StarExpr:
		return DeterminantExprOfIsTypeConversion(e.(*ast.StarExpr).X)
	case *ast.CallExpr:
		return DeterminantExprOfIsTypeConversion(e.(*ast.CallExpr).Fun)
	case *ast.SelectorExpr:
		return e.(*ast.SelectorExpr).Sel
	default:
		return e
	}
}

func IsTypeConversion(info *types.Info, e *ast.CallExpr) bool {
	if info == nil {
		return false
	}

	funcOrType := DeterminantExprOfIsTypeConversion(e)

	switch funcOrType.(type) {
	case *ast.ChanType, *ast.FuncType, *ast.MapType, *ast.ArrayType, *ast.StructType, *ast.InterfaceType:
		return true
	case *ast.Ident:
		id := funcOrType.(*ast.Ident)

		if t, ok := info.Types[id]; ok {
			return t.IsType()
		}

		if o := info.ObjectOf(id); o != nil {
			switch o.(type) {
			case *types.TypeName:
				return true
			default:
				return false
			}
		}
	}

	return false
}
<|endoftext|>


<commit_before>// Copyright 2016 CoreOS, Inc.
//
// 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 e2e

import "testing"

func TestCtlV3RoleAdd(t *testing.T)          { testCtl(t, roleAddTest) }
func TestCtlV3RoleAddNoTLS(t *testing.T)     { testCtl(t, roleAddTest, withCfg(configNoTLS)) }
func TestCtlV3RoleAddClientTLS(t *testing.T) { testCtl(t, roleAddTest, withCfg(configClientTLS)) }
func TestCtlV3RoleAddPeerTLS(t *testing.T)   { testCtl(t, roleAddTest, withCfg(configPeerTLS)) }
func TestCtlV3RoleAddTimeout(t *testing.T)   { testCtl(t, roleAddTest, withDialTimeout(0)) }

func roleAddTest(cx ctlCtx) {
	cmdSet := []struct {
		args        []string
		expectedStr string
	}{
		{
			args:        []string{"add", "root"},
			expectedStr: "Role root created",
		},
		{
			args:        []string{"add", "root"},
			expectedStr: "role name already exists",
		},
	}

	for i, cmd := range cmdSet {
		if err := ctlV3Role(cx, cmd.args, cmd.expectedStr); err != nil {
			if cx.dialTimeout > 0 && !isGRPCTimedout(err) {
				cx.t.Fatalf("roleAddTest #%d: ctlV3Role error (%v)", i, err)
			}
		}
	}
}

func ctlV3Role(cx ctlCtx, args []string, expStr string) error {
	cmdArgs := append(cx.PrefixArgs(), "role")
	cmdArgs = append(cmdArgs, args...)

	return spawnWithExpect(cmdArgs, expStr)
}
<commit_msg>e2e: Test case for the etcdctlv3 'role grant' command.<commit_after>// Copyright 2016 CoreOS, Inc.
//
// 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 e2e

import "testing"

func TestCtlV3RoleAdd(t *testing.T)          { testCtl(t, roleAddTest) }
func TestCtlV3RoleAddNoTLS(t *testing.T)     { testCtl(t, roleAddTest, withCfg(configNoTLS)) }
func TestCtlV3RoleAddClientTLS(t *testing.T) { testCtl(t, roleAddTest, withCfg(configClientTLS)) }
func TestCtlV3RoleAddPeerTLS(t *testing.T)   { testCtl(t, roleAddTest, withCfg(configPeerTLS)) }
func TestCtlV3RoleAddTimeout(t *testing.T)   { testCtl(t, roleAddTest, withDialTimeout(0)) }

func TestCtlV3RoleGrant(t *testing.T) { testCtl(t, roleGrantTest) }

func roleAddTest(cx ctlCtx) {
	cmdSet := []struct {
		args        []string
		expectedStr string
	}{
		// Add a role.
		{
			args:        []string{"add", "root"},
			expectedStr: "Role root created",
		},
		// Try adding the same role.
		{
			args:        []string{"add", "root"},
			expectedStr: "role name already exists",
		},
	}

	for i, cmd := range cmdSet {
		if err := ctlV3Role(cx, cmd.args, cmd.expectedStr); err != nil {
			if cx.dialTimeout > 0 && !isGRPCTimedout(err) {
				cx.t.Fatalf("roleAddTest #%d: ctlV3Role error (%v)", i, err)
			}
		}
	}
}

func roleGrantTest(cx ctlCtx) {
	cmdSet := []struct {
		args        []string
		expectedStr string
	}{
		// Add a role.
		{
			args:        []string{"add", "root"},
			expectedStr: "Role root created",
		},
		// Grant read permission to the role.
		{
			args:        []string{"grant", "root", "read", "foo"},
			expectedStr: "Role root updated",
		},
		// Grant write permission to the role.
		{
			args:        []string{"grant", "root", "write", "foo"},
			expectedStr: "Role root updated",
		},
		// Grant rw permission to the role.
		{
			args:        []string{"grant", "root", "readwrite", "foo"},
			expectedStr: "Role root updated",
		},
		// Try granting invalid permission to the role.
		{
			args:        []string{"grant", "root", "123", "foo"},
			expectedStr: "invalid permission type",
		},
	}

	for i, cmd := range cmdSet {
		if err := ctlV3Role(cx, cmd.args, cmd.expectedStr); err != nil {
			cx.t.Fatalf("roleGrantTest #%d: ctlV3Role error (%v)", i, err)
		}
	}
}

func ctlV3Role(cx ctlCtx, args []string, expStr string) error {
	cmdArgs := append(cx.PrefixArgs(), "role")
	cmdArgs = append(cmdArgs, args...)

	return spawnWithExpect(cmdArgs, expStr)
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 the original author or authors.
 *
 * 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 main

import (
	"context"
	"fmt"
	"gopkg.in/Shopify/sarama.v1"
	"github.com/bsm/sarama-cluster"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"
	"github.com/satori/go.uuid"
	"github.com/projectriff/http-gateway/pkg/message"
	"syscall"
	"sync"
)

// Function messageHandler creates an http handler that posts the http body as a message to Kafka, replying
// immediately with a successful http response
func messageHandler(producer sarama.AsyncProducer) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		topic := r.URL.Path[len("/messages/"):]
		b, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		scsMessage := message.Message{Payload: b, Headers: headersToPropagate(r)}

		bytesOut, err := message.EncodeMessage(scsMessage)
		if err != nil {
			log.Printf("Error encoding message: %v", err)
			return
		}
		kafkaMsg := &sarama.ProducerMessage{Topic: topic, Value: sarama.ByteEncoder(bytesOut)}

		select {
		case producer.Input() <- kafkaMsg:
			w.Write([]byte("message published to topic: " + topic + "\n"))
		}
	}
}

// Function replyHandler creates an http handler that posts the http body as a message to Kafka, then waits
// for a message on a go channel it creates for a reply (this is expected to be set by the main thread) and sends
// that as an http response.
func replyHandler(producer sarama.AsyncProducer, replies repliesMap) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		topic := r.URL.Path[len("/requests/"):]
		b, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		correlationId := uuid.NewV4().String()
		replyChan := make(chan message.Message)
		replies.put(correlationId, replyChan)
		headers := headersToPropagate(r)
		headers["correlationId"] = correlationId
		scsMessage := message.Message{Payload: b, Headers: headers}

		bytesOut, err := message.EncodeMessage(scsMessage)
		if err != nil {
			log.Printf("Error encoding message: %v", err)
			return
		}
		kafkaMsg := &sarama.ProducerMessage{Topic: topic, Value: sarama.ByteEncoder(bytesOut)}

		select {
		case producer.Input() <- kafkaMsg:
			select {
			case reply := <-replyChan:
				replies.delete(correlationId)
				p := reply.Payload
				for k, v := range reply.Headers {
					switch value := v.(type) {
					case string:
						w.Header()[k] = []string{value}
					case []string:
						w.Header()[k] = value
					}
				}
				w.Write(p.([]byte)) // TODO equivalent of Spring's HttpMessageConverter handling
			case <- time.After(time.Second * 60):
				replies.delete(correlationId)
				w.WriteHeader(404)
			}
		}
	}
}

func healthHandler() func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`{"status":"UP"}`))
	}
}

func startHttpServer(producer sarama.AsyncProducer, replies repliesMap) *http.Server {
	srv := &http.Server{Addr: ":8080"}

	http.HandleFunc("/messages/", messageHandler(producer))
	http.HandleFunc("/requests/", replyHandler(producer, replies))
	http.HandleFunc("/application/status", healthHandler())

	go func() {
		if err := srv.ListenAndServe(); err != nil {
			log.Printf("Httpserver: ListenAndServe() error: %s", err)
		}
	}()

	log.Printf("Listening on %v", srv.Addr)
	return srv
}

func headersToPropagate(r *http.Request) map[string]interface{} {
	result := make(map[string]interface{})
	if ct, ok := r.Header["Content-Type"]; ok {
		result["Content-Type"] = ct[0]
	}
	if ct, ok := r.Header["Accept"]; ok {
		result["Accept"] = ct[0]
	}
	return result
}

func main() {
	// Trap signals to trigger a proper shutdown.
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt, syscall.SIGTERM, os.Kill)

	// Key is correlationId, value is channel used to pass message received from main Kafka consumer loop
	replies := newRepliesMap()

	brokers := []string{os.Getenv("SPRING_CLOUD_STREAM_KAFKA_BINDER_BROKERS")}
	producer, err := sarama.NewAsyncProducer(brokers, nil)
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := producer.Close(); err != nil {
			log.Fatalln(err)
		}
	}()

	consumerConfig := makeConsumerConfig()
	consumer, err := cluster.NewConsumer(brokers, "gateway", []string{"replies"}, consumerConfig)
	if err != nil {
		panic(err)
	}
	defer consumer.Close()
	if consumerConfig.Consumer.Return.Errors {
		go consumeErrors(consumer)
	}
	if consumerConfig.Group.Return.Notifications {
		go consumeNotifications(consumer)
	}

	srv := startHttpServer(producer, replies)

MainLoop:
	for {
		select {
		case <-signals:
			fmt.Println("Shutting Down...")
			timeout, c := context.WithTimeout(context.Background(), 1*time.Second)
			defer c()
			if err := srv.Shutdown(timeout); err != nil {
				panic(err) // failure/timeout shutting down the server gracefully
			}
			break MainLoop
		case msg, ok := <-consumer.Messages():
			if ok {
				messageWithHeaders, err := message.ExtractMessage(msg.Value)
				if err != nil {
					log.Println("Failed to extract message ", err)
					break
				}
				correlationId, ok := messageWithHeaders.Headers["correlationId"].(string)
				if ok {
					c := replies.get(correlationId)
					if c != nil {
						fmt.Printf("Sending %v\n", messageWithHeaders)
						c <- messageWithHeaders
						consumer.MarkOffset(msg, "") // mark message as processed
					} else {
						log.Printf("Did not find communication channel for correlationId %v. Timed out?", correlationId)
						consumer.MarkOffset(msg, "") // mark message as processed
					}
				}
			}

		case err := <-producer.Errors():
			log.Println("Failed to produce kafka message ", err)
		}
	}
}

func makeConsumerConfig() *cluster.Config {
	consumerConfig := cluster.NewConfig()
	consumerConfig.Consumer.Return.Errors = true
	consumerConfig.Group.Return.Notifications = true
	return consumerConfig
}

func consumeNotifications(consumer *cluster.Consumer) {
	for ntf := range consumer.Notifications() {
		log.Printf("Rebalanced: %+v\n", ntf)
	}
}
func consumeErrors(consumer *cluster.Consumer) {
	for err := range consumer.Errors() {
		log.Printf("Error: %s\n", err.Error())
	}
}

// Type repliesMap implements a concurrent safe map of channels to send replies to, keyed by message correlationIds
type repliesMap struct {
	m map[string] chan<- message.Message
	lock sync.RWMutex
}

func (replies *repliesMap) delete(key string) {
	replies.lock.Lock()
	defer replies.lock.Unlock()
	delete(replies.m, key)
}

func (replies *repliesMap) get(key string) chan<- message.Message {
	replies.lock.RLock()
	defer replies.lock.RUnlock()
	return replies.m[key]
}

func (replies *repliesMap) put(key string, value chan<- message.Message) {
	replies.lock.Lock()
	defer replies.lock.Unlock()
	replies.m[key] = value
}

func newRepliesMap() repliesMap {
	return repliesMap{make(map[string]chan<- message.Message), sync.RWMutex{}}
}<commit_msg>Limit headers that are sent back to client<commit_after>/*
 * Copyright 2017 the original author or authors.
 *
 * 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 main

import (
	"context"
	"gopkg.in/Shopify/sarama.v1"
	"github.com/bsm/sarama-cluster"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"
	"github.com/satori/go.uuid"
	"github.com/projectriff/http-gateway/pkg/message"
	"syscall"
	"sync"
)

const ContentType = "Content-Type"
const Accept = "Accept"
const CorrelationId = "correlationId"

var incomingHeadersToPropagate = [...]string{ContentType, Accept}
var outgoingHeadersToPropagate = [...]string{ContentType}

// Function messageHandler creates an http handler that posts the http body as a message to Kafka, replying
// immediately with a successful http response
func messageHandler(producer sarama.AsyncProducer) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		topic := r.URL.Path[len("/messages/"):]
		b, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		msg := message.Message{Payload: b, Headers: make(map[string]interface{})}
		propagateIncomingHeaders(r, &msg)

		bytesOut, err := message.EncodeMessage(msg)
		if err != nil {
			log.Printf("Error encoding message: %v", err)
			return
		}
		kafkaMsg := &sarama.ProducerMessage{Topic: topic, Value: sarama.ByteEncoder(bytesOut)}

		select {
		case producer.Input() <- kafkaMsg:
			w.Write([]byte("message published to topic: " + topic + "\n"))
		}
	}
}

// Function replyHandler creates an http handler that posts the http body as a message to Kafka, then waits
// for a message on a go channel it creates for a reply (this is expected to be set by the main thread) and sends
// that as an http response.
func replyHandler(producer sarama.AsyncProducer, replies *repliesMap) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		topic := r.URL.Path[len("/requests/"):]
		b, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		correlationId := uuid.NewV4().String()
		replyChan := make(chan message.Message)
		replies.put(correlationId, replyChan)

		msg := message.Message{Payload: b, Headers: make(map[string]interface{})}
		propagateIncomingHeaders(r, &msg)
		msg.Headers[CorrelationId] = correlationId

		bytesOut, err := message.EncodeMessage(msg)
		if err != nil {
			log.Printf("Error encoding message: %v", err)
			return
		}
		kafkaMsg := &sarama.ProducerMessage{Topic: topic, Value: sarama.ByteEncoder(bytesOut)}

		select {
		case producer.Input() <- kafkaMsg:
			select {
			case reply := <-replyChan:
				replies.delete(correlationId)
				p := reply.Payload
				propagateOutgoingHeaders(&reply, w)
				w.Write(p.([]byte)) // TODO equivalent of Spring's HttpMessageConverter handling
			case <-time.After(time.Second * 60):
				replies.delete(correlationId)
				w.WriteHeader(404)
			}
		}
	}
}
func healthHandler() func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`{"status":"UP"}`))
	}
}

func startHttpServer(producer sarama.AsyncProducer, replies *repliesMap) *http.Server {
	srv := &http.Server{Addr: ":8080"}

	http.HandleFunc("/messages/", messageHandler(producer))
	http.HandleFunc("/requests/", replyHandler(producer, replies))
	http.HandleFunc("/application/status", healthHandler())

	go func() {
		if err := srv.ListenAndServe(); err != nil {
			log.Printf("Httpserver: ListenAndServe() error: %s", err)
		}
	}()

	log.Printf("Listening on %v", srv.Addr)
	return srv
}

func propagateIncomingHeaders(request *http.Request, message *message.Message) {
	for _, h := range incomingHeadersToPropagate {
		if v, ok := request.Header[h]; ok {
			message.Headers[h] = v[0]
		}
	}
}

func propagateOutgoingHeaders(message *message.Message, response http.ResponseWriter) {
	for _, h := range outgoingHeadersToPropagate {
		if v, ok := message.Headers[h]; ok {
			switch value := v.(type) {
			case string:
				response.Header()[h] = []string{value}
			case []string:
				response.Header()[h] = value
			}
		}
	}
}

func main() {
	// Trap signals to trigger a proper shutdown.
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt, syscall.SIGTERM, os.Kill)

	// Key is correlationId, value is channel used to pass message received from main Kafka consumer loop
	replies := newRepliesMap()

	brokers := []string{os.Getenv("SPRING_CLOUD_STREAM_KAFKA_BINDER_BROKERS")}
	producer, err := sarama.NewAsyncProducer(brokers, nil)
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := producer.Close(); err != nil {
			log.Fatalln(err)
		}
	}()

	consumerConfig := makeConsumerConfig()
	consumer, err := cluster.NewConsumer(brokers, "gateway", []string{"replies"}, consumerConfig)
	if err != nil {
		panic(err)
	}
	defer consumer.Close()
	if consumerConfig.Consumer.Return.Errors {
		go consumeErrors(consumer)
	}
	if consumerConfig.Group.Return.Notifications {
		go consumeNotifications(consumer)
	}

	srv := startHttpServer(producer, replies)

MainLoop:
	for {
		select {
		case <-signals:
			log.Println("Shutting Down...")
			timeout, c := context.WithTimeout(context.Background(), 1*time.Second)
			defer c()
			if err := srv.Shutdown(timeout); err != nil {
				panic(err) // failure/timeout shutting down the server gracefully
			}
			break MainLoop
		case msg, ok := <-consumer.Messages():
			if ok {
				messageWithHeaders, err := message.ExtractMessage(msg.Value)
				if err != nil {
					log.Println("Failed to extract message ", err)
					break
				}
				correlationId, ok := messageWithHeaders.Headers["correlationId"].(string)
				if ok {
					c := replies.get(correlationId)
					if c != nil {
						log.Printf("Sending %v\n", messageWithHeaders)
						c <- messageWithHeaders
						consumer.MarkOffset(msg, "") // mark message as processed
					} else {
						log.Printf("Did not find communication channel for correlationId %v. Timed out?", correlationId)
						consumer.MarkOffset(msg, "") // mark message as processed
					}
				}
			}

		case err := <-producer.Errors():
			log.Println("Failed to produce kafka message ", err)
		}
	}
}

func makeConsumerConfig() *cluster.Config {
	consumerConfig := cluster.NewConfig()
	consumerConfig.Consumer.Return.Errors = true
	consumerConfig.Group.Return.Notifications = true
	return consumerConfig
}

func consumeNotifications(consumer *cluster.Consumer) {
	for ntf := range consumer.Notifications() {
		log.Printf("Rebalanced: %+v\n", ntf)
	}
}
func consumeErrors(consumer *cluster.Consumer) {
	for err := range consumer.Errors() {
		log.Printf("Error: %s\n", err.Error())
	}
}

// Type repliesMap implements a concurrent safe map of channels to send replies to, keyed by message correlationIds
type repliesMap struct {
	m    map[string]chan<- message.Message
	lock sync.RWMutex
}

func (replies *repliesMap) delete(key string) {
	replies.lock.Lock()
	defer replies.lock.Unlock()
	delete(replies.m, key)
}

func (replies *repliesMap) get(key string) chan<- message.Message {
	replies.lock.RLock()
	defer replies.lock.RUnlock()
	return replies.m[key]
}

func (replies *repliesMap) put(key string, value chan<- message.Message) {
	replies.lock.Lock()
	defer replies.lock.Unlock()
	replies.m[key] = value
}

func newRepliesMap() *repliesMap {
	return &repliesMap{make(map[string]chan<- message.Message), sync.RWMutex{}}
}
<|endoftext|>


<commit_before>// Copyright 2014 CoreOS, Inc.
//
// 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 cli

import (
	"os"

	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/spf13/cobra"

	"github.com/coreos/mantle/system/exec"
	"github.com/coreos/mantle/version"
)

var (
	versionCmd = &cobra.Command{
		Use:   "version",
		Short: "Print the version number and exit.",
		Run: func(cmd *cobra.Command, args []string) {
			cmd.Printf("mantle/%s version %s\n",
				cmd.Root().Name(), version.Version)
		},
	}

	logDebug   bool
	logVerbose bool
	logLevel   = capnslog.NOTICE

	plog = capnslog.NewPackageLogger("github.com/coreos/mantle", "cli")
)

// Execute sets up common features that all mantle commands should share
// and then executes the command. It does not return.
func Execute(main *cobra.Command) {
	// If we were invoked via a multicall entrypoint run it instead.
	// TODO(marineam): should we figure out a way to initialize logging?
	exec.MaybeExec()

	main.AddCommand(versionCmd)

	// TODO(marineam): pflags defines the Value interface differently,
	// update capnslog accordingly...
	//main.PersistentFlags().Var(&level, "log-level",
	//	"Set global log level. (default is NOTICE)")
	main.PersistentFlags().BoolVarP(&logVerbose, "verbose", "v", false,
		"Alias for --log-level=INFO")
	main.PersistentFlags().BoolVarP(&logDebug, "debug", "d", false,
		"Alias for --log-level=DEBUG")

	var preRun = main.PersistentPreRun
	main.PersistentPreRun = func(cmd *cobra.Command, args []string) {
		startLogging(cmd)
		if preRun != nil {
			preRun(cmd, args)
		}
	}

	if err := main.Execute(); err != nil {
		plog.Fatal(err)
	}
	os.Exit(0)
}

func startLogging(cmd *cobra.Command) {
	switch {
	case logDebug:
		logLevel = capnslog.DEBUG
	case logVerbose:
		logLevel = capnslog.INFO
	}

	capnslog.SetFormatter(capnslog.NewStringFormatter(cmd.Out()))
	capnslog.SetGlobalLogLevel(logLevel)
	plog.Infof("Started logging at level %s", logLevel)
}
<commit_msg>cli: silence etcd messages at default log level<commit_after>// Copyright 2014 CoreOS, Inc.
//
// 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 cli

import (
	"os"

	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/spf13/cobra"

	"github.com/coreos/mantle/system/exec"
	"github.com/coreos/mantle/version"
)

var (
	versionCmd = &cobra.Command{
		Use:   "version",
		Short: "Print the version number and exit.",
		Run: func(cmd *cobra.Command, args []string) {
			cmd.Printf("mantle/%s version %s\n",
				cmd.Root().Name(), version.Version)
		},
	}

	logDebug   bool
	logVerbose bool
	logLevel   = capnslog.NOTICE

	plog = capnslog.NewPackageLogger("github.com/coreos/mantle", "cli")
)

// Execute sets up common features that all mantle commands should share
// and then executes the command. It does not return.
func Execute(main *cobra.Command) {
	// If we were invoked via a multicall entrypoint run it instead.
	// TODO(marineam): should we figure out a way to initialize logging?
	exec.MaybeExec()

	main.AddCommand(versionCmd)

	// TODO(marineam): pflags defines the Value interface differently,
	// update capnslog accordingly...
	//main.PersistentFlags().Var(&level, "log-level",
	//	"Set global log level. (default is NOTICE)")
	main.PersistentFlags().BoolVarP(&logVerbose, "verbose", "v", false,
		"Alias for --log-level=INFO")
	main.PersistentFlags().BoolVarP(&logDebug, "debug", "d", false,
		"Alias for --log-level=DEBUG")

	var preRun = main.PersistentPreRun
	main.PersistentPreRun = func(cmd *cobra.Command, args []string) {
		startLogging(cmd)
		if preRun != nil {
			preRun(cmd, args)
		}
	}

	if err := main.Execute(); err != nil {
		plog.Fatal(err)
	}
	os.Exit(0)
}

func setRepoLogLevel(repo string, l capnslog.LogLevel) {
	r, err := capnslog.GetRepoLogger(repo)
	if err != nil {
		return // don't care if it isn't linked in
	}
	r.SetRepoLogLevel(l)
}

func startLogging(cmd *cobra.Command) {
	switch {
	case logDebug:
		logLevel = capnslog.DEBUG
	case logVerbose:
		logLevel = capnslog.INFO
	}

	capnslog.SetFormatter(capnslog.NewStringFormatter(cmd.Out()))
	capnslog.SetGlobalLogLevel(logLevel)

	// In the context of the internally linked etcd, the NOTICE messages
	// aren't really interesting, so translate NOTICE to WARNING instead.
	if logLevel == capnslog.NOTICE {
		// etcd sure has a lot of repos in its repo
		setRepoLogLevel("github.com/coreos/etcd", capnslog.WARNING)
		setRepoLogLevel("github.com/coreos/etcd/etcdserver", capnslog.WARNING)
		setRepoLogLevel("github.com/coreos/etcd/etcdserver/etcdhttp", capnslog.WARNING)
		setRepoLogLevel("github.com/coreos/etcd/pkg", capnslog.WARNING)
	}

	plog.Infof("Started logging at level %s", logLevel)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"strings"

	"github.com/grokify/swaggman/openapi3"
	"github.com/grokify/swaggman/openapi3/openapi3postman2"
	"github.com/jessevdk/go-flags"
)

// Convert yaml2json: https://github.com/bronze1man/yaml2json ... yaml2json_darwin_amd64

type Options struct {
	Config      string `short:"c" long:"config" description:"Swaggman Config File"`
	PostmanBase string `short:"b" long:"base" description:"Basic Postman File"`
	Postman     string `short:"p" long:"postman" description:"Output Postman File" required:"true"`
	OpenAPIFile string `short:"o" long:"openapi" description:"Input Swagger File" required:"true"`
}

func (opts *Options) TrimSpace() {
	opts.Config = strings.TrimSpace(opts.Config)
	opts.PostmanBase = strings.TrimSpace(opts.PostmanBase)
	opts.Postman = strings.TrimSpace(opts.Postman)
	opts.OpenAPIFile = strings.TrimSpace(opts.OpenAPIFile)
}

func main() {
	var opts Options
	_, err := flags.Parse(&opts)
	if err != nil {
		log.Fatal(err)
	}

	cfg3 := openapi3postman2.Configuration{}

	if len(opts.Config) > 0 {
		cfg3, err = openapi3postman2.ConfigurationReadFile(opts.Config)
		if err != nil {
			log.Fatal(err)
		}
	}

	spec, err := openapi3.ReadFile(opts.OpenAPIFile, true)
	if err != nil {
		log.Fatal(err)
	}
	conv := openapi3postman2.Converter{
		Configuration: cfg3,
		OpenAPISpec:   spec}

	err = conv.MergeConvert(
		opts.OpenAPIFile,
		opts.PostmanBase,
		opts.Postman)

	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("wrote Postman collection [%s]", opts.Postman)
	fmt.Println("DONE")
}
<commit_msg>fix: cli: cli formatting<commit_after>package main

import (
	"fmt"
	"log"
	"strings"

	"github.com/grokify/swaggman/openapi3"
	"github.com/grokify/swaggman/openapi3/openapi3postman2"
	"github.com/jessevdk/go-flags"
)

// Convert yaml2json: https://github.com/bronze1man/yaml2json ... yaml2json_darwin_amd64

type Options struct {
	Config      string `short:"c" long:"config" description:"Swaggman Config File"`
	PostmanBase string `short:"b" long:"base" description:"Basic Postman File"`
	Postman     string `short:"p" long:"postman" description:"Output Postman File" required:"true"`
	OpenAPIFile string `short:"o" long:"openapi" description:"Input Swagger File" required:"true"`
}

func (opts *Options) TrimSpace() {
	opts.Config = strings.TrimSpace(opts.Config)
	opts.PostmanBase = strings.TrimSpace(opts.PostmanBase)
	opts.Postman = strings.TrimSpace(opts.Postman)
	opts.OpenAPIFile = strings.TrimSpace(opts.OpenAPIFile)
}

func main() {
	var opts Options
	_, err := flags.Parse(&opts)
	if err != nil {
		log.Fatal(err)
	}

	cfg3 := openapi3postman2.Configuration{}

	if len(opts.Config) > 0 {
		cfg3, err = openapi3postman2.ConfigurationReadFile(opts.Config)
		if err != nil {
			log.Fatal(err)
		}
	}

	spec, err := openapi3.ReadFile(opts.OpenAPIFile, true)
	if err != nil {
		log.Fatal(err)
	}
	conv := openapi3postman2.Converter{
		Configuration: cfg3,
		OpenAPISpec:   spec}

	err = conv.MergeConvert(
		opts.OpenAPIFile,
		opts.PostmanBase,
		opts.Postman)

	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("wrote Postman collection [%s]\n", opts.Postman)
	fmt.Println("DONE")
}
<|endoftext|>


<commit_before>package jws

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/rsa"
	"errors"
	"hash"
	"net/url"

	"github.com/lestrrat/go-jwx/buffer"
	"github.com/lestrrat/go-jwx/jwa"
	"github.com/lestrrat/go-jwx/jwk"
)

var (
	ErrInvalidCompactPartsCount  = errors.New("compact JWS format must have three parts")
	ErrInvalidEcdsaSignatureSize = errors.New("invalid signature size of ecdsa algorithm")
	ErrInvalidSignature          = errors.New("invalid signature")
	ErrMissingPrivateKey         = errors.New("missing private key")
	ErrMissingPublicKey          = errors.New("missing public key")
	ErrUnsupportedAlgorithm      = errors.New("unspported algorithm")
)

// Base64Encoder can encode itself into base64. But you can do more such as
// filling default values, validating them, etc. This is used in `Encode()`
// as both headers and payloads
type Base64Encoder interface {
	Base64Encode() ([]byte, error)
}

type Base64Decoder interface {
	Base64Decode([]byte) error
}

type EssentialHeader struct {
	Algorithm              jwa.SignatureAlgorithm `json:"alg,omitempty"`
	ContentType            string                 `json:"cty,omitempty"`
	Critical               []string               `json:"crit,omitempty"`
	Jwk                    jwk.JSONWebKey         `json:"jwk,omitempty"` // public key
	JwkSetURL              *url.URL               `json:"jku,omitempty"`
	KeyID                  string                 `json:"kid,omitempty"`
	Type                   string                 `json:"typ,omitempty"` // e.g. "JWT"
	X509Url                *url.URL               `json:"x5u,omitempty"`
	X509CertChain          []string               `json:"x5c,omitempty"`
	X509CertThumbprint     string                 `json:"x5t,omitempty"`
	X509CertThumbprintS256 string                 `json:"x5t#S256,omitempty"`
}

// Header represents a jws header.
type Header struct {
	*EssentialHeader `json:"-"`
	PrivateParams    map[string]interface{} `json:"-"`
}

// EncodedHeader represents a header value that is base64 encoded
// in JSON format
type EncodedHeader struct {
	*Header
	// This is a special field. It's ONLY set when parsed from a serialized form.
	// It's used for verification purposes, because header representations (such as
	// JSON key order) may differ from what the source encoded with and what the
	// go json package uses
	//
	// If this field is populated (Len() > 0), it will be used for signature
	// calculation.
	// If you change the header values, make sure to clear this field, too
	Source buffer.Buffer  `json:"-"`
}

// PayloadSigner generates signature for the given payload
type PayloadSigner interface {
	Jwk() jwk.JSONWebKey
	Kid() string
	Alg() jwa.SignatureAlgorithm
	PayloadSign([]byte) ([]byte, error)
}

// Verifier is used to verify the signature against the payload
type Verifier interface {
	Verify([]byte, []byte) error
}

type RsaSign struct {
	Algorithm  jwa.SignatureAlgorithm
	JSONWebKey *jwk.RsaPublicKey
	KeyID      string
	PrivateKey *rsa.PrivateKey
}

type EcdsaSign struct {
	Algorithm  jwa.SignatureAlgorithm
	JSONWebKey *jwk.RsaPublicKey
	KeyID      string
	PrivateKey *ecdsa.PrivateKey
}

type MergedHeader struct {
	ProtectedHeader *EncodedHeader
	PublicHeader    *Header
}

type Signature struct {
	PublicHeader    *Header        `json:"header"`              // Raw JWS Unprotected Heders
	ProtectedHeader *EncodedHeader `json:"protected,omitempty"` // Base64 encoded JWS Protected Headers
	Signature       buffer.Buffer  `json:"signature"`           // Base64 encoded signature
}

// Message represents a full JWS encoded message. Flattened serialization
// is not supported as a struct, but rather it's represented as a
// Message struct with only one `signature` element
type Message struct {
	Payload    buffer.Buffer `json:"payload"`
	Signatures []Signature   `json:"signatures"`
}

type MultiSigner interface {
	MultiSign(buffer.Buffer) (*Message, error)
	AddSigner(PayloadSigner)
}

type MultiSign struct {
	Signers []PayloadSigner
}

type HmacSign struct {
	Algorithm  jwa.SignatureAlgorithm
	JSONWebKey *jwk.RsaPublicKey
	KeyID      string
	Key        []byte
	hash       func() hash.Hash
}

type Serializer interface {
	Serialize(*Message) ([]byte, error)
}

type CompactSerialize struct{}
type JSONSerialize struct {
	Pretty bool
}

type RsaVerify struct {
	alg    jwa.SignatureAlgorithm
	hash   crypto.Hash
	pubkey *rsa.PublicKey
}

type EcdsaVerify struct {
	alg    jwa.SignatureAlgorithm
	hash   crypto.Hash
	pubkey *ecdsa.PublicKey
}
<commit_msg>remove unused interface<commit_after>package jws

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/rsa"
	"errors"
	"hash"
	"net/url"

	"github.com/lestrrat/go-jwx/buffer"
	"github.com/lestrrat/go-jwx/jwa"
	"github.com/lestrrat/go-jwx/jwk"
)

var (
	ErrInvalidCompactPartsCount  = errors.New("compact JWS format must have three parts")
	ErrInvalidEcdsaSignatureSize = errors.New("invalid signature size of ecdsa algorithm")
	ErrInvalidSignature          = errors.New("invalid signature")
	ErrMissingPrivateKey         = errors.New("missing private key")
	ErrMissingPublicKey          = errors.New("missing public key")
	ErrUnsupportedAlgorithm      = errors.New("unspported algorithm")
)

// Base64Encoder can encode itself into base64. But you can do more such as
// filling default values, validating them, etc. This is used in `Encode()`
// as both headers and payloads
type Base64Encoder interface {
	Base64Encode() ([]byte, error)
}

type Base64Decoder interface {
	Base64Decode([]byte) error
}

type EssentialHeader struct {
	Algorithm              jwa.SignatureAlgorithm `json:"alg,omitempty"`
	ContentType            string                 `json:"cty,omitempty"`
	Critical               []string               `json:"crit,omitempty"`
	Jwk                    jwk.JSONWebKey         `json:"jwk,omitempty"` // public key
	JwkSetURL              *url.URL               `json:"jku,omitempty"`
	KeyID                  string                 `json:"kid,omitempty"`
	Type                   string                 `json:"typ,omitempty"` // e.g. "JWT"
	X509Url                *url.URL               `json:"x5u,omitempty"`
	X509CertChain          []string               `json:"x5c,omitempty"`
	X509CertThumbprint     string                 `json:"x5t,omitempty"`
	X509CertThumbprintS256 string                 `json:"x5t#S256,omitempty"`
}

// Header represents a jws header.
type Header struct {
	*EssentialHeader `json:"-"`
	PrivateParams    map[string]interface{} `json:"-"`
}

// EncodedHeader represents a header value that is base64 encoded
// in JSON format
type EncodedHeader struct {
	*Header
	// This is a special field. It's ONLY set when parsed from a serialized form.
	// It's used for verification purposes, because header representations (such as
	// JSON key order) may differ from what the source encoded with and what the
	// go json package uses
	//
	// If this field is populated (Len() > 0), it will be used for signature
	// calculation.
	// If you change the header values, make sure to clear this field, too
	Source buffer.Buffer  `json:"-"`
}

// PayloadSigner generates signature for the given payload
type PayloadSigner interface {
	Jwk() jwk.JSONWebKey
	Kid() string
	Alg() jwa.SignatureAlgorithm
	PayloadSign([]byte) ([]byte, error)
}

// Verifier is used to verify the signature against the payload
type Verifier interface {
	Verify([]byte, []byte) error
}

type RsaSign struct {
	Algorithm  jwa.SignatureAlgorithm
	JSONWebKey *jwk.RsaPublicKey
	KeyID      string
	PrivateKey *rsa.PrivateKey
}

type EcdsaSign struct {
	Algorithm  jwa.SignatureAlgorithm
	JSONWebKey *jwk.RsaPublicKey
	KeyID      string
	PrivateKey *ecdsa.PrivateKey
}

type MergedHeader struct {
	ProtectedHeader *EncodedHeader
	PublicHeader    *Header
}

type Signature struct {
	PublicHeader    *Header        `json:"header"`              // Raw JWS Unprotected Heders
	ProtectedHeader *EncodedHeader `json:"protected,omitempty"` // Base64 encoded JWS Protected Headers
	Signature       buffer.Buffer  `json:"signature"`           // Base64 encoded signature
}

// Message represents a full JWS encoded message. Flattened serialization
// is not supported as a struct, but rather it's represented as a
// Message struct with only one `signature` element
type Message struct {
	Payload    buffer.Buffer `json:"payload"`
	Signatures []Signature   `json:"signatures"`
}

type MultiSign struct {
	Signers []PayloadSigner
}

type HmacSign struct {
	Algorithm  jwa.SignatureAlgorithm
	JSONWebKey *jwk.RsaPublicKey
	KeyID      string
	Key        []byte
	hash       func() hash.Hash
}

type Serializer interface {
	Serialize(*Message) ([]byte, error)
}

type CompactSerialize struct{}
type JSONSerialize struct {
	Pretty bool
}

type RsaVerify struct {
	alg    jwa.SignatureAlgorithm
	hash   crypto.Hash
	pubkey *rsa.PublicKey
}

type EcdsaVerify struct {
	alg    jwa.SignatureAlgorithm
	hash   crypto.Hash
	pubkey *ecdsa.PublicKey
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"os"
	"path"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/bitrise/version"
	"github.com/bitrise-io/stepman/stepman"
	"github.com/codegangsta/cli"
)

func initLogFormatter() {
	log.SetFormatter(&log.TextFormatter{
		ForceColors:     true,
		FullTimestamp:   true,
		TimestampFormat: "15:04:05",
	})
}

func before(c *cli.Context) error {
	initLogFormatter()
	initHelpAndVersionFlags()
	initAppHelpTemplate()

	// Log level
	if logLevel, err := log.ParseLevel(c.String(LogLevelKey)); err != nil {
		log.Fatal("[BITRISE_CLI] - Failed to parse log level:", err)
	} else {
		log.SetLevel(logLevel)
	}

	// Setup
	err := stepman.CreateStepManDirIfNeeded()
	if err != nil {
		return err
	}

	// Debug mode
	stepman.DebugMode = c.Bool(DebugKey)
	return nil
}

func printVersion(c *cli.Context) {
	fmt.Fprintf(c.App.Writer, "%v\n", c.App.Version)
}

// Run ...
func Run() {
	cli.VersionPrinter = printVersion

	app := cli.NewApp()
	app.Name = path.Base(os.Args[0])
	app.Usage = "Step manager"
	app.Version = version.VERSION

	app.Author = ""
	app.Email = ""

	app.Before = before

	app.Flags = flags
	app.Commands = commands

	if err := app.Run(os.Args); err != nil {
		log.Error("[STEPMAN] - Stepman finished:", err)
	}
}
<commit_msg>PR fix<commit_after>package cli

import (
	"fmt"
	"os"
	"path"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/stepman/stepman"
	"github.com/bitrise-io/stepman/version"
	"github.com/codegangsta/cli"
)

func initLogFormatter() {
	log.SetFormatter(&log.TextFormatter{
		ForceColors:     true,
		FullTimestamp:   true,
		TimestampFormat: "15:04:05",
	})
}

func before(c *cli.Context) error {
	initLogFormatter()
	initHelpAndVersionFlags()
	initAppHelpTemplate()

	// Log level
	if logLevel, err := log.ParseLevel(c.String(LogLevelKey)); err != nil {
		log.Fatal("[BITRISE_CLI] - Failed to parse log level:", err)
	} else {
		log.SetLevel(logLevel)
	}

	// Setup
	err := stepman.CreateStepManDirIfNeeded()
	if err != nil {
		return err
	}

	// Debug mode
	stepman.DebugMode = c.Bool(DebugKey)
	return nil
}

func printVersion(c *cli.Context) {
	fmt.Fprintf(c.App.Writer, "%v\n", c.App.Version)
}

// Run ...
func Run() {
	cli.VersionPrinter = printVersion

	app := cli.NewApp()
	app.Name = path.Base(os.Args[0])
	app.Usage = "Step manager"
	app.Version = version.VERSION

	app.Author = ""
	app.Email = ""

	app.Before = before

	app.Flags = flags
	app.Commands = commands

	if err := app.Run(os.Args); err != nil {
		log.Error("[STEPMAN] - Stepman finished:", err)
	}
}
<|endoftext|>