<commit_before>package director

import (
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"time"

	"github.com/Bplotka/oidc/authorize"
	"github.com/mwitkow/go-conntrack"
	"github.com/mwitkow/go-httpwares"
	"github.com/mwitkow/go-httpwares/logging/logrus"
	"github.com/mwitkow/go-httpwares/metrics/prometheus"
	"github.com/mwitkow/go-httpwares/reporter"
	"github.com/mwitkow/go-httpwares/tags"
	"github.com/mwitkow/kedge/http/backendpool"
	"github.com/mwitkow/kedge/http/director/adhoc"
	"github.com/mwitkow/kedge/http/director/proxyreq"
	"github.com/mwitkow/kedge/http/director/router"
	"github.com/mwitkow/kedge/lib/http/ctxtags"
	"github.com/mwitkow/kedge/lib/http/tripperware"
	"github.com/mwitkow/kedge/lib/sharedflags"
	"github.com/oxtoacart/bpool"
	"github.com/sirupsen/logrus"
	"github.com/mwitkow/go-httpwares/metrics"
)

var (
	AdhocTransport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
			DualStack: true,
		}).DialContext,
		MaxIdleConns:          100,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
	}

	flagBufferSizeBytes  = sharedflags.Set.Int("http_reverseproxy_buffer_size_bytes", 32*1024, "Size (bytes) of reusable buffer used for copying HTTP reverse proxy responses.")
	flagBufferCount      = sharedflags.Set.Int("http_reverseproxy_buffer_count", 2*1024, "Maximum number of of reusable buffer used for copying HTTP reverse proxy responses.")
	flagFlushingInterval = sharedflags.Set.Duration("http_reverseproxy_flushing_interval", 10*time.Millisecond, "Interval for flushing the responses in HTTP reverse proxy code.")
)

// New creates a forward/reverse proxy that is either Route+Backend and Adhoc Rules forwarding.
//
// The Router decides which "well-known" routes a given request matches, and which backend from the Pool it should be
// sent to. The backends in the Pool have pre-dialed connections and are load balanced.
//
// If  Adhoc routing supports dialing to whitelisted DNS names either through DNS A or SRV records for undefined backends.
func New(pool backendpool.Pool, router router.Router, addresser adhoc.Addresser, logEntry logrus.FieldLogger) *Proxy {
	AdhocTransport.DialContext = conntrack.NewDialContextFunc(conntrack.DialWithName("adhoc"), conntrack.DialWithTracing())
	bufferpool := bpool.NewBytePool(*flagBufferCount, *flagBufferSizeBytes)

	clientMetrics := http_prometheus.ClientMetrics(http_prometheus.WithLatency())
	backendTripper := http_metrics.Tripperware(clientMetrics)(&backendPoolTripper{pool: pool})
	adhocTripper := http_metrics.Tripperware(clientMetrics)(AdhocTransport)

	p := &Proxy{
		backendReverseProxy: &httputil.ReverseProxy{
			Director:      func(r *http.Request) {},
			Transport:     backendTripper,
			FlushInterval: *flagFlushingInterval,
			BufferPool:    bufferpool,
			ErrorLog:      http_logrus.AsHttpLogger(logEntry.WithField("caller", "backend reverseProxy")),
		},
		adhocReverseProxy: &httputil.ReverseProxy{
			Director:      func(r *http.Request) {},
			Transport:     adhocTripper,
			FlushInterval: *flagFlushingInterval,
			BufferPool:    bufferpool,
			ErrorLog:      http_logrus.AsHttpLogger(logEntry.WithField("caller", "adhoc reverseProxy")),
		},
		router:    router,
		addresser: addresser,
	}
	return p
}

// Proxy is a forward/reverse proxy that implements Route+Backend and Adhoc Rules forwarding.
type Proxy struct {
	router    router.Router
	addresser adhoc.Addresser

	backendReverseProxy *httputil.ReverseProxy
	adhocReverseProxy   *httputil.ReverseProxy
}

func (p *Proxy) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
	if _, ok := resp.(http.Flusher); !ok {
		panic("the http.ResponseWriter passed must be an http.Flusher")
	}
	// note resp needs to implement Flusher, otherwise flush intervals won't work.
	normReq := proxyreq.NormalizeInboundRequest(req)
	backend, err := p.router.Route(req)
	tags := http_ctxtags.ExtractInbound(req)
	tags.Set(http_ctxtags.TagForCallService, "proxy")
	if err == nil {
		resp.Header().Set("x-kedge-backend-name", backend)
		tags.Set(ctxtags.TagForProxyBackend, backend)
		tags.Set(http_ctxtags.TagForHandlerName, backend)
		normReq.URL.Host = backend
		p.backendReverseProxy.ServeHTTP(resp, normReq)
		return
	} else if err != router.ErrRouteNotFound {
		respondWithError(err, req, resp)
		return
	}
	addr, err := p.addresser.Address(req)
	if err == nil {
		normReq.URL.Host = addr
		tags.Set(ctxtags.TagForProxyAdhoc, addr)
		tags.Set(http_ctxtags.TagForHandlerName, "_adhoc")
		p.adhocReverseProxy.ServeHTTP(resp, normReq)
		return
	}
	respondWithError(err, req, resp)
}

// backendPoolTripper assumes the response has been rewritten by the proxy to have the backend as req.URL.Host
type backendPoolTripper struct {
	pool backendpool.Pool
}

func (t *backendPoolTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	tripper, err := t.pool.Tripper(req.URL.Host)
	if err != nil {
		return nil, err
	}
	return tripper.RoundTrip(req)
}

func respondWithError(err error, req *http.Request, resp http.ResponseWriter) {
	status := http.StatusBadGateway
	if rErr, ok := (err).(*router.Error); ok {
		status = rErr.StatusCode()
	}
	http_ctxtags.ExtractInbound(req).Set(logrus.ErrorKey, err)
	resp.Header().Set("x-kedge-error", err.Error())
	resp.Header().Set("content-type", "text/plain")
	resp.WriteHeader(status)
	fmt.Fprintf(resp, "%v", err.Error())
}

func AuthMiddleware(authorizer authorize.Authorizer) httpwares.Middleware {
	return func(nextHandler http.Handler) http.Handler {
		return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
			err := authorize.IsRequestAuthorized(req, authorizer, tripperware.ProxyAuthHeader)
			if err != nil {
				respondWithUnauthorized(err, req, resp)
				return
			}
			// Request authorized - continue.
			nextHandler.ServeHTTP(resp, req)
		})
	}
}

func respondWithUnauthorized(err error, req *http.Request, resp http.ResponseWriter) {
	status := http.StatusUnauthorized
	http_ctxtags.ExtractInbound(req).Set(logrus.ErrorKey, err)
	resp.Header().Set("x-kedge-error", err.Error())
	resp.Header().Set("content-type", "text/plain")
	resp.WriteHeader(status)
}
<commit_msg>Import fix.<commit_after>package director

import (
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"time"

	"github.com/Bplotka/oidc/authorize"
	"github.com/mwitkow/go-conntrack"
	"github.com/mwitkow/go-httpwares"
	"github.com/mwitkow/go-httpwares/logging/logrus"
	"github.com/mwitkow/go-httpwares/metrics"
	"github.com/mwitkow/go-httpwares/metrics/prometheus"
	"github.com/mwitkow/go-httpwares/tags"
	"github.com/mwitkow/kedge/http/backendpool"
	"github.com/mwitkow/kedge/http/director/adhoc"
	"github.com/mwitkow/kedge/http/director/proxyreq"
	"github.com/mwitkow/kedge/http/director/router"
	"github.com/mwitkow/kedge/lib/http/ctxtags"
	"github.com/mwitkow/kedge/lib/http/tripperware"
	"github.com/mwitkow/kedge/lib/sharedflags"
	"github.com/oxtoacart/bpool"
	"github.com/sirupsen/logrus"
)

var (
	AdhocTransport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
			DualStack: true,
		}).DialContext,
		MaxIdleConns:          100,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
	}

	flagBufferSizeBytes  = sharedflags.Set.Int("http_reverseproxy_buffer_size_bytes", 32*1024, "Size (bytes) of reusable buffer used for copying HTTP reverse proxy responses.")
	flagBufferCount      = sharedflags.Set.Int("http_reverseproxy_buffer_count", 2*1024, "Maximum number of of reusable buffer used for copying HTTP reverse proxy responses.")
	flagFlushingInterval = sharedflags.Set.Duration("http_reverseproxy_flushing_interval", 10*time.Millisecond, "Interval for flushing the responses in HTTP reverse proxy code.")
)

// New creates a forward/reverse proxy that is either Route+Backend and Adhoc Rules forwarding.
//
// The Router decides which "well-known" routes a given request matches, and which backend from the Pool it should be
// sent to. The backends in the Pool have pre-dialed connections and are load balanced.
//
// If  Adhoc routing supports dialing to whitelisted DNS names either through DNS A or SRV records for undefined backends.
func New(pool backendpool.Pool, router router.Router, addresser adhoc.Addresser, logEntry logrus.FieldLogger) *Proxy {
	AdhocTransport.DialContext = conntrack.NewDialContextFunc(conntrack.DialWithName("adhoc"), conntrack.DialWithTracing())
	bufferpool := bpool.NewBytePool(*flagBufferCount, *flagBufferSizeBytes)

	clientMetrics := http_prometheus.ClientMetrics(http_prometheus.WithLatency())
	backendTripper := http_metrics.Tripperware(clientMetrics)(&backendPoolTripper{pool: pool})
	adhocTripper := http_metrics.Tripperware(clientMetrics)(AdhocTransport)

	p := &Proxy{
		backendReverseProxy: &httputil.ReverseProxy{
			Director:      func(r *http.Request) {},
			Transport:     backendTripper,
			FlushInterval: *flagFlushingInterval,
			BufferPool:    bufferpool,
			ErrorLog:      http_logrus.AsHttpLogger(logEntry.WithField("caller", "backend reverseProxy")),
		},
		adhocReverseProxy: &httputil.ReverseProxy{
			Director:      func(r *http.Request) {},
			Transport:     adhocTripper,
			FlushInterval: *flagFlushingInterval,
			BufferPool:    bufferpool,
			ErrorLog:      http_logrus.AsHttpLogger(logEntry.WithField("caller", "adhoc reverseProxy")),
		},
		router:    router,
		addresser: addresser,
	}
	return p
}

// Proxy is a forward/reverse proxy that implements Route+Backend and Adhoc Rules forwarding.
type Proxy struct {
	router    router.Router
	addresser adhoc.Addresser

	backendReverseProxy *httputil.ReverseProxy
	adhocReverseProxy   *httputil.ReverseProxy
}

func (p *Proxy) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
	if _, ok := resp.(http.Flusher); !ok {
		panic("the http.ResponseWriter passed must be an http.Flusher")
	}
	// note resp needs to implement Flusher, otherwise flush intervals won't work.
	normReq := proxyreq.NormalizeInboundRequest(req)
	backend, err := p.router.Route(req)
	tags := http_ctxtags.ExtractInbound(req)
	tags.Set(http_ctxtags.TagForCallService, "proxy")
	if err == nil {
		resp.Header().Set("x-kedge-backend-name", backend)
		tags.Set(ctxtags.TagForProxyBackend, backend)
		tags.Set(http_ctxtags.TagForHandlerName, backend)
		normReq.URL.Host = backend
		p.backendReverseProxy.ServeHTTP(resp, normReq)
		return
	} else if err != router.ErrRouteNotFound {
		respondWithError(err, req, resp)
		return
	}
	addr, err := p.addresser.Address(req)
	if err == nil {
		normReq.URL.Host = addr
		tags.Set(ctxtags.TagForProxyAdhoc, addr)
		tags.Set(http_ctxtags.TagForHandlerName, "_adhoc")
		p.adhocReverseProxy.ServeHTTP(resp, normReq)
		return
	}
	respondWithError(err, req, resp)
}

// backendPoolTripper assumes the response has been rewritten by the proxy to have the backend as req.URL.Host
type backendPoolTripper struct {
	pool backendpool.Pool
}

func (t *backendPoolTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	tripper, err := t.pool.Tripper(req.URL.Host)
	if err != nil {
		return nil, err
	}
	return tripper.RoundTrip(req)
}

func respondWithError(err error, req *http.Request, resp http.ResponseWriter) {
	status := http.StatusBadGateway
	if rErr, ok := (err).(*router.Error); ok {
		status = rErr.StatusCode()
	}
	http_ctxtags.ExtractInbound(req).Set(logrus.ErrorKey, err)
	resp.Header().Set("x-kedge-error", err.Error())
	resp.Header().Set("content-type", "text/plain")
	resp.WriteHeader(status)
	fmt.Fprintf(resp, "%v", err.Error())
}

func AuthMiddleware(authorizer authorize.Authorizer) httpwares.Middleware {
	return func(nextHandler http.Handler) http.Handler {
		return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
			err := authorize.IsRequestAuthorized(req, authorizer, tripperware.ProxyAuthHeader)
			if err != nil {
				respondWithUnauthorized(err, req, resp)
				return
			}
			// Request authorized - continue.
			nextHandler.ServeHTTP(resp, req)
		})
	}
}

func respondWithUnauthorized(err error, req *http.Request, resp http.ResponseWriter) {
	status := http.StatusUnauthorized
	http_ctxtags.ExtractInbound(req).Set(logrus.ErrorKey, err)
	resp.Header().Set("x-kedge-error", err.Error())
	resp.Header().Set("content-type", "text/plain")
	resp.WriteHeader(status)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"net/http"

	"github.com/julienschmidt/httprouter"
)

/* Read functions
 */
func ListLevel(w http.ResponseWriter, r *http.Request,
	_ httprouter.Params) {
	defer PanicCatcher(w)

	returnChannel := make(chan somaResult)
	handler := handlerMap["levelReadHandler"].(somaLevelReadHandler)
	handler.input <- somaLevelRequest{
		action: "list",
		reply:  returnChannel,
	}
	result := <-returnChannel

	// declase here since goto does not jump over declarations
	cReq := proto.NewLevelFilter()
	if result.Failure() {
		goto skip
	}

	_ = DecodeJsonBody(r, &cReq)
	if (cReq.Filter.Level.Name != "") || (cReq.Filter.Level.ShortName != "") {
		filtered := make([]somaLevelResult, 0)
		for _, i := range result.Levels {
			if ((cReq.Filter.Level.Name != "") && (cReq.Filter.Level.Name == i.Level.Name)) ||
				((cReq.Filter.Level.ShortName != "") && (cReq.Filter.Level.ShortName == i.Level.ShortName)) {
				filtered = append(filtered, i)
			}
		}
		result.Levels = filtered
	}

skip:
	SendLevelReply(&w, &result)
}

func ShowLevel(w http.ResponseWriter, r *http.Request,
	params httprouter.Params) {
	defer PanicCatcher(w)

	returnChannel := make(chan somaResult)
	handler := handlerMap["levelReadHandler"].(somaLevelReadHandler)
	handler.input <- somaLevelRequest{
		action: "show",
		reply:  returnChannel,
		Level: proto.Level{
			Name: params.ByName("level"),
		},
	}
	result := <-returnChannel
	SendLevelReply(&w, &result)
}

/* Write functions
 */
func AddLevel(w http.ResponseWriter, r *http.Request,
	_ httprouter.Params) {
	defer PanicCatcher(w)

	cReq := proto.Request{}
	err := DecodeJsonBody(r, &cReq)
	if err != nil {
		DispatchBadRequest(&w, err)
		return
	}

	returnChannel := make(chan somaResult)
	handler := handlerMap["levelWriteHandler"].(somaLevelWriteHandler)
	handler.input <- somaLevelRequest{
		action: "add",
		reply:  returnChannel,
		Level: proto.Level{
			Name:      cReq.Level.Name,
			ShortName: cReq.Level.ShortName,
			Numeric:   cReq.Level.Numeric,
		},
	}
	result := <-returnChannel
	SendLevelReply(&w, &result)
}

func DeleteLevel(w http.ResponseWriter, r *http.Request,
	params httprouter.Params) {
	defer PanicCatcher(w)

	returnChannel := make(chan somaResult)
	handler := handlerMap["levelWriteHandler"].(somaLevelWriteHandler)
	handler.input <- somaLevelRequest{
		action: "delete",
		reply:  returnChannel,
		Level: proto.Level{
			Name: params.ByName("level"),
		},
	}
	result := <-returnChannel
	SendLevelReply(&w, &result)
}

/* Utility
 */
func SendLevelReply(w *http.ResponseWriter, r *somaResult) {
	result := proto.NewLevelResult()
	if r.MarkErrors(&result) {
		goto dispatch
	}
	for _, i := range (*r).Levels {
		*result.Levels = append(*result.Levels, i.Level)
		if i.ResultError != nil {
			*result.Errors = append(*result.Errors, i.ResultError.Error())
		}
	}

dispatch:
	json, err := json.Marshal(result)
	if err != nil {
		DispatchInternalError(w, err)
		return
	}
	DispatchJsonReply(w, &json)
	return
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Add permission check: levels<commit_after>package main

import (
	"encoding/json"
	"net/http"

	"github.com/julienschmidt/httprouter"
)

/* Read functions
 */
func ListLevel(w http.ResponseWriter, r *http.Request,
	params httprouter.Params) {
	defer PanicCatcher(w)
	if ok, _ := IsAuthorized(params.ByName(`AuthenticatedUser`),
		`levels_list`, ``, ``, ``); !ok {
		DispatchForbidden(&w, nil)
		return
	}

	returnChannel := make(chan somaResult)
	handler := handlerMap["levelReadHandler"].(somaLevelReadHandler)
	handler.input <- somaLevelRequest{
		action: "list",
		reply:  returnChannel,
	}
	result := <-returnChannel

	// declase here since goto does not jump over declarations
	cReq := proto.NewLevelFilter()
	if result.Failure() {
		goto skip
	}

	_ = DecodeJsonBody(r, &cReq)
	if (cReq.Filter.Level.Name != "") || (cReq.Filter.Level.ShortName != "") {
		filtered := make([]somaLevelResult, 0)
		for _, i := range result.Levels {
			if ((cReq.Filter.Level.Name != "") && (cReq.Filter.Level.Name == i.Level.Name)) ||
				((cReq.Filter.Level.ShortName != "") && (cReq.Filter.Level.ShortName == i.Level.ShortName)) {
				filtered = append(filtered, i)
			}
		}
		result.Levels = filtered
	}

skip:
	SendLevelReply(&w, &result)
}

func ShowLevel(w http.ResponseWriter, r *http.Request,
	params httprouter.Params) {
	defer PanicCatcher(w)
	if ok, _ := IsAuthorized(params.ByName(`AuthenticatedUser`),
		`levels_show`, ``, ``, ``); !ok {
		DispatchForbidden(&w, nil)
		return
	}

	returnChannel := make(chan somaResult)
	handler := handlerMap["levelReadHandler"].(somaLevelReadHandler)
	handler.input <- somaLevelRequest{
		action: "show",
		reply:  returnChannel,
		Level: proto.Level{
			Name: params.ByName("level"),
		},
	}
	result := <-returnChannel
	SendLevelReply(&w, &result)
}

/* Write functions
 */
func AddLevel(w http.ResponseWriter, r *http.Request,
	params httprouter.Params) {
	defer PanicCatcher(w)
	if ok, _ := IsAuthorized(params.ByName(`AuthenticatedUser`),
		`levels_create`, ``, ``, ``); !ok {
		DispatchForbidden(&w, nil)
		return
	}

	cReq := proto.Request{}
	err := DecodeJsonBody(r, &cReq)
	if err != nil {
		DispatchBadRequest(&w, err)
		return
	}

	returnChannel := make(chan somaResult)
	handler := handlerMap["levelWriteHandler"].(somaLevelWriteHandler)
	handler.input <- somaLevelRequest{
		action: "add",
		reply:  returnChannel,
		Level: proto.Level{
			Name:      cReq.Level.Name,
			ShortName: cReq.Level.ShortName,
			Numeric:   cReq.Level.Numeric,
		},
	}
	result := <-returnChannel
	SendLevelReply(&w, &result)
}

func DeleteLevel(w http.ResponseWriter, r *http.Request,
	params httprouter.Params) {
	defer PanicCatcher(w)
	if ok, _ := IsAuthorized(params.ByName(`AuthenticatedUser`),
		`levels_delete`, ``, ``, ``); !ok {
		DispatchForbidden(&w, nil)
		return
	}

	returnChannel := make(chan somaResult)
	handler := handlerMap["levelWriteHandler"].(somaLevelWriteHandler)
	handler.input <- somaLevelRequest{
		action: "delete",
		reply:  returnChannel,
		Level: proto.Level{
			Name: params.ByName("level"),
		},
	}
	result := <-returnChannel
	SendLevelReply(&w, &result)
}

/* Utility
 */
func SendLevelReply(w *http.ResponseWriter, r *somaResult) {
	result := proto.NewLevelResult()
	if r.MarkErrors(&result) {
		goto dispatch
	}
	for _, i := range (*r).Levels {
		*result.Levels = append(*result.Levels, i.Level)
		if i.ResultError != nil {
			*result.Errors = append(*result.Errors, i.ResultError.Error())
		}
	}

dispatch:
	json, err := json.Marshal(result)
	if err != nil {
		DispatchInternalError(w, err)
		return
	}
	DispatchJsonReply(w, &json)
	return
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package momos

import (
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"

	"github.com/hemerajs/momos/logger"
	"github.com/lox/httpcache"
)

const (
	version = "1.0.0"
	website = "https://github.com/hemerajs/momos"
	banner  = `
__  ___
/  |/  /__  __ _  ___  ___
/ /|_/ / _ \/  ' \/ _ \(_-<
/_/  /_/\___/_/_/_/\___/___/ %s
High performance, reverse proxy for advanced SSI
`
)

var ServerLogging = false
var Log = logger.NewStdLogger(true, true, true, true, false)

type Proxy struct {
	ReverseProxy *httputil.ReverseProxy
	server       *http.Server
	ProxyURL     string
	Handler      *httpcache.Handler
}

func New(targetUrl string) *Proxy {
	target, tErr := url.Parse(targetUrl)

	if tErr != nil {
		Log.Errorf("Invalid url: %v", targetUrl)
		panic(tErr)
	}

	httpcache.DebugLogging = ServerLogging

	p := &Proxy{}
	p.ReverseProxy = httputil.NewSingleHostReverseProxy(target)
	p.ReverseProxy.Transport = &proxyTransport{http.DefaultTransport}

	return p
}

func (p *Proxy) Start(addr string) error {
	// create proxy server
	server := &http.Server{
		Addr:         addr,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	p.server = server
	// assign roundTrip handler
	p.server.Handler = p.ReverseProxy

	fmt.Printf(banner, version)

	// start server
	return server.ListenAndServe()
}
<commit_msg>remove dep<commit_after>package momos

import (
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"

	"github.com/hemerajs/momos/logger"
	"github.com/lox/httpcache"
)

const (
	version = "1.0.0"
	website = "https://github.com/hemerajs/momos"
	banner  = `
__  ___
/  |/  /__  __ _  ___  ___
/ /|_/ / _ \/  ' \/ _ \(_-<
/_/  /_/\___/_/_/_/\___/___/ %s
High performance, reverse proxy for advanced SSI
`
)

var ServerLogging = false
var Log = logger.NewStdLogger(true, true, true, true, false)

type Proxy struct {
	ReverseProxy *httputil.ReverseProxy
	server       *http.Server
	ProxyURL     string
	Handler      *httpcache.Handler
}

func New(targetUrl string) *Proxy {
	target, tErr := url.Parse(targetUrl)

	if tErr != nil {
		Log.Errorf("Invalid url: %v", targetUrl)
		panic(tErr)
	}

	p := &Proxy{}
	p.ReverseProxy = httputil.NewSingleHostReverseProxy(target)
	p.ReverseProxy.Transport = &proxyTransport{http.DefaultTransport}

	return p
}

func (p *Proxy) Start(addr string) error {
	// create proxy server
	server := &http.Server{
		Addr:         addr,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	p.server = server
	// assign roundTrip handler
	p.server.Handler = p.ReverseProxy

	fmt.Printf(banner, version)

	// start server
	return server.ListenAndServe()
}
<|endoftext|>


<commit_before>package rel

import (
	"log"
	"runtime/debug"
)

// Used to handle generating Postgres specific sql
type SQLiteVisitor struct {
	Conn Connector
}

func (v SQLiteVisitor) Accept(visitable Visitable) string {
	return v.Visit(visitable)
}

func (v SQLiteVisitor) Visit(visitable Visitable) string {
	switch node := visitable.(type) {
	case nil:
		return visitationNil()
	case *SelectStatementNode:
		return v.VisitSelectStatementNode(node)
	case *InNode:
		return visitationInNode(v, node)
	case SqlLiteralNode:
		return visitationSqlLiteralNode(v, node)
	case *SqlLiteralNode:
		return visitationSqlLiteralNode(v, *node)
	case *JoinSource:
		return visitationJoinSourceNode(v, node)
	case *EqualityNode:
		return visitationEqualityNode(v, node)
	case *HavingNode:
		return visitationHavingNode(v, node)
	case *AttributeNode:
		return visitationAttributeNode(v, node)
	case *GroupNode:
		return visitationGroupNode(v, node)
	case *ExistsNode:
		return visitationExistsNode(v, node)
	case *AsNode:
		return visitationAsNode(v, node)
	case *LessThanNode:
		return visitationLessThanNode(v, node)
	case *UnionNode:
		return visitationUnionNode(v, node)
	case *UnionAllNode:
		return visitationUnionAllNode(v, node)
	case *SelectManager:
		return visitationSelectManager(v, node)
	case *GreaterThanNode:
		return visitationGreaterThanNode(v, node)
	case *IntersectNode:
		return visitationIntersectNode(v, node)
	case *ExceptNode:
		return visitationExceptNode(v, node)
	case *OnNode:
		return visitationOnNode(v, node)
	case *AscendingNode:
		return visitationAscendingNode(v, node)
	case *DescendingNode:
		return visitationDescendingNode(v, node)
	case *CountNode:
		return visitationCountNode(v, node)
	case *AndNode:
		return visitationAndNode(v, node)
	case *TableAliasNode:
		return visitationTableAliasNode(v, node)
	case *InnerJoinNode:
		return visitationInnerJoinNode(v, node)
	case *GroupingNode:
		return visitationGroupingNode(v, node)
	case *NamedWindowNode:
		return visitationNamedWindowNode(v, node)
	case *WindowNode:
		return visitationWindowNode(v, node)
	case *RowsNode:
		return visitationRowsNode(v, node)
	case *LockNode:
		return v.VisitLockNode(node)
	case *PrecedingNode:
		return visitationPrecedingNode(v, node)
	case *FollowingNode:
		return visitationFollowingNode(v, node)
	case *CurrentRowNode:
		return visitationCurrentRowNode(v, node)
	case *BetweenNode:
		return visitationBetweenNode(v, node)
	case *RangeNode:
		return visitationRangeNode(v, node)
	case *DistinctNode:
		return visitationDistinctNode(v, node)
	case *WithNode:
		return visitationWithNode(v, node)
	case *WithRecursiveNode:
		return visitationWithRecursiveNode(v, node)
	case *Table:
		if node == nil {
			return visitationNil()
		}
		return visitationTable(v, node)
	case *MultiStatementManager:
		return visitationMultiStatementManager(v, node)
	case *InsertStatementNode:
		return visitationInsertStatementNode(v, node)
	case *SelectCoreNode:
		return visitationSelectCoreNode(v, node)
	case *NotEqualNode:
		return visitationNotEqualNode(v, node)
	case *NotNode:
		return visitationNotNode(v, node)
	case *GreaterThanOrEqualNode:
		return visitationGreaterThanOrEqualNode(v, node)
	case *LessThanOrEqualNode:
		return visitationLessThanOrEqualNode(v, node)
	case *OrNode:
		return visitationOrNode(v, node)
	case *AvgNode:
		return visitationAvgNode(v, node)
	case *NamedFunctionNode:
		return visitationNamedFunctionNode(v, node)
	case *SumNode:
		return visitationSumNode(v, node)
	case *MinNode:
		return visitationMinNode(v, node)
	case *MaxNode:
		return visitationMaxNode(v, node)
	case *MatchesNode:
		return visitationMatchesNode(v, node)
	case *DoesNotMatchNode:
		return visitationDoesNotMatchNode(v, node)
	case *NotInNode:
		return visitationNotInNode(v, node)
	case *BinNode:
		return visitationBinNode(v, node)
	case *ExtractNode:
		return visitationExtractNode(v, node)
	case *InfixOperationNode:
		return visitationInfixOperationNode(v, node)
	case *QuotedNode:
		return visitationQuotedNode(v, node)
	case *OverNode:
		return visitationOverNode(v, node)
	case *AssignmentNode:
		return visitationAssignmentNode(v, node)
	case *UnqualifiedColumnNode:
		return visitationUnqualifiedColumnNode(v, node)
	case *DistinctOnNode:
		return visitationDistinctOnNode(v, node)
	case *OuterJoinNode:
		return visitationOuterJoinNode(v, node)
	case *OffsetNode:
		return visitationOffsetNode(v, node)
	case *LimitNode:
		return visitationLimitNode(v, node)
	case *UpdateStatementNode:
		return visitationUpdateStatementNode(v, node)
	case *DeleteStatementNode:
		return visitationDeleteStatementNode(v, node)
	case *FalseNode:
		return visitationFalseNode(v, node)
	case *TrueNode:
		return visitationTrueNode(v, node)
	case *ValuesNode:
		return visitationValuesNode(v, node)
	case *OrderingNode:
		return visitationOrderingNode(v, node)
	case *TopNode:
		return visitationTopNode(v, node)
	default:
		debug.PrintStack()
		log.Fatalf("SQLiteVisitor#Visit unable to handle type %T", visitable)
		return ""
	}
}

func (v SQLiteVisitor) QuoteTableName(visitable Visitable) string {
	if alias, ok := visitable.(*TableAliasNode); ok {
		if !alias.Quoted {
			return alias.Name.Raw
		}
	}
	return v.Conn.QuoteTableName(visitable.String())
}

func (v SQLiteVisitor) Quote(thing interface{}) string {
	return v.Conn.Quote(thing)
}

func (v SQLiteVisitor) QuoteColumnName(literal SqlLiteralNode) string {
	return v.Conn.QuoteColumnName(literal.Raw)
}

// VisitLockNode is overwritten for the SQLiteVisitor
// Locks are not supported in SQLite
func (v *SQLiteVisitor) VisitLockNode(node *LockNode) string {
	return ""
}

func (v *SQLiteVisitor) VisitSelectStatementNode(node *SelectStatementNode) string {
	if node.Offset != nil && node.Limit == nil {
		node.Limit = &LimitNode{Expr: Sql("-1")}
	}
	return visitationSelectStatementNode(v, node)
}
<commit_msg>Change visibility for a few methods in SQLiteVisitor<commit_after>package rel

import (
	"log"
	"runtime/debug"
)

// Used to handle generating Postgres specific sql
type SQLiteVisitor struct {
	Conn Connector
}

func (v SQLiteVisitor) Accept(visitable Visitable) string {
	return v.Visit(visitable)
}

func (v SQLiteVisitor) Visit(visitable Visitable) string {
	switch node := visitable.(type) {
	case nil:
		return visitationNil()
	case *SelectStatementNode:
		return v.visitSelectStatementNode(node)
	case *InNode:
		return visitationInNode(v, node)
	case SqlLiteralNode:
		return visitationSqlLiteralNode(v, node)
	case *SqlLiteralNode:
		return visitationSqlLiteralNode(v, *node)
	case *JoinSource:
		return visitationJoinSourceNode(v, node)
	case *EqualityNode:
		return visitationEqualityNode(v, node)
	case *HavingNode:
		return visitationHavingNode(v, node)
	case *AttributeNode:
		return visitationAttributeNode(v, node)
	case *GroupNode:
		return visitationGroupNode(v, node)
	case *ExistsNode:
		return visitationExistsNode(v, node)
	case *AsNode:
		return visitationAsNode(v, node)
	case *LessThanNode:
		return visitationLessThanNode(v, node)
	case *UnionNode:
		return visitationUnionNode(v, node)
	case *UnionAllNode:
		return visitationUnionAllNode(v, node)
	case *SelectManager:
		return visitationSelectManager(v, node)
	case *GreaterThanNode:
		return visitationGreaterThanNode(v, node)
	case *IntersectNode:
		return visitationIntersectNode(v, node)
	case *ExceptNode:
		return visitationExceptNode(v, node)
	case *OnNode:
		return visitationOnNode(v, node)
	case *AscendingNode:
		return visitationAscendingNode(v, node)
	case *DescendingNode:
		return visitationDescendingNode(v, node)
	case *CountNode:
		return visitationCountNode(v, node)
	case *AndNode:
		return visitationAndNode(v, node)
	case *TableAliasNode:
		return visitationTableAliasNode(v, node)
	case *InnerJoinNode:
		return visitationInnerJoinNode(v, node)
	case *GroupingNode:
		return visitationGroupingNode(v, node)
	case *NamedWindowNode:
		return visitationNamedWindowNode(v, node)
	case *WindowNode:
		return visitationWindowNode(v, node)
	case *RowsNode:
		return visitationRowsNode(v, node)
	case *LockNode:
		return v.visitLockNode(node)
	case *PrecedingNode:
		return visitationPrecedingNode(v, node)
	case *FollowingNode:
		return visitationFollowingNode(v, node)
	case *CurrentRowNode:
		return visitationCurrentRowNode(v, node)
	case *BetweenNode:
		return visitationBetweenNode(v, node)
	case *RangeNode:
		return visitationRangeNode(v, node)
	case *DistinctNode:
		return visitationDistinctNode(v, node)
	case *WithNode:
		return visitationWithNode(v, node)
	case *WithRecursiveNode:
		return visitationWithRecursiveNode(v, node)
	case *Table:
		if node == nil {
			return visitationNil()
		}
		return visitationTable(v, node)
	case *MultiStatementManager:
		return visitationMultiStatementManager(v, node)
	case *InsertStatementNode:
		return visitationInsertStatementNode(v, node)
	case *SelectCoreNode:
		return visitationSelectCoreNode(v, node)
	case *NotEqualNode:
		return visitationNotEqualNode(v, node)
	case *NotNode:
		return visitationNotNode(v, node)
	case *GreaterThanOrEqualNode:
		return visitationGreaterThanOrEqualNode(v, node)
	case *LessThanOrEqualNode:
		return visitationLessThanOrEqualNode(v, node)
	case *OrNode:
		return visitationOrNode(v, node)
	case *AvgNode:
		return visitationAvgNode(v, node)
	case *NamedFunctionNode:
		return visitationNamedFunctionNode(v, node)
	case *SumNode:
		return visitationSumNode(v, node)
	case *MinNode:
		return visitationMinNode(v, node)
	case *MaxNode:
		return visitationMaxNode(v, node)
	case *MatchesNode:
		return visitationMatchesNode(v, node)
	case *DoesNotMatchNode:
		return visitationDoesNotMatchNode(v, node)
	case *NotInNode:
		return visitationNotInNode(v, node)
	case *BinNode:
		return visitationBinNode(v, node)
	case *ExtractNode:
		return visitationExtractNode(v, node)
	case *InfixOperationNode:
		return visitationInfixOperationNode(v, node)
	case *QuotedNode:
		return visitationQuotedNode(v, node)
	case *OverNode:
		return visitationOverNode(v, node)
	case *AssignmentNode:
		return visitationAssignmentNode(v, node)
	case *UnqualifiedColumnNode:
		return visitationUnqualifiedColumnNode(v, node)
	case *DistinctOnNode:
		return visitationDistinctOnNode(v, node)
	case *OuterJoinNode:
		return visitationOuterJoinNode(v, node)
	case *OffsetNode:
		return visitationOffsetNode(v, node)
	case *LimitNode:
		return visitationLimitNode(v, node)
	case *UpdateStatementNode:
		return visitationUpdateStatementNode(v, node)
	case *DeleteStatementNode:
		return visitationDeleteStatementNode(v, node)
	case *FalseNode:
		return visitationFalseNode(v, node)
	case *TrueNode:
		return visitationTrueNode(v, node)
	case *ValuesNode:
		return visitationValuesNode(v, node)
	case *OrderingNode:
		return visitationOrderingNode(v, node)
	case *TopNode:
		return visitationTopNode(v, node)
	default:
		debug.PrintStack()
		log.Fatalf("SQLiteVisitor#Visit unable to handle type %T", visitable)
		return ""
	}
}

func (v SQLiteVisitor) QuoteTableName(visitable Visitable) string {
	if alias, ok := visitable.(*TableAliasNode); ok {
		if !alias.Quoted {
			return alias.Name.Raw
		}
	}
	return v.Conn.QuoteTableName(visitable.String())
}

func (v SQLiteVisitor) Quote(thing interface{}) string {
	return v.Conn.Quote(thing)
}

func (v SQLiteVisitor) QuoteColumnName(literal SqlLiteralNode) string {
	return v.Conn.QuoteColumnName(literal.Raw)
}

// VisitLockNode is overwritten for the SQLiteVisitor
// Locks are not supported in SQLite
func (v *SQLiteVisitor) visitLockNode(node *LockNode) string {
	return ""
}

func (v *SQLiteVisitor) visitSelectStatementNode(node *SelectStatementNode) string {
	if node.Offset != nil && node.Limit == nil {
		node.Limit = &LimitNode{Expr: Sql("-1")}
	}
	return visitationSelectStatementNode(v, node)
}
<|endoftext|>


<commit_before>package libkbfs

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"time"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/logger"
)

const (
	// LocalDynamoDBDownloadURI source: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Tools.DynamoDBLocal.html
	// We don't use latest because Amazon doesn't offer https downloads of this. So we peg to a revision and verify the hash.
	LocalDynamoDBDownloadURI = "http://dynamodb-local.s3-website-us-west-2.amazonaws.com/dynamodb_local_2015-07-16_1.0.tar.gz"
	// LocalDynamoDBSha256Hash is the sha256 hash of the above tar ball.
	LocalDynamoDBSha256Hash = "5868fd4b9f624001cda88059af7a54f412a4794dea0d3497e7c57470bfb272fa"
	// LocalDynamoDBTmpDir is relative to the system's own TempDir.
	LocalDynamoDBTmpDir = "dynamodb_local"
	// LocalDynamoDBPidFile contains the process ID.
	LocalDynamoDBPidFile = "dynamodb.pid"
	// LocalDynamoDBJarFile is the name of the local dynamodb server jar file.
	LocalDynamoDBJarFile = "DynamoDBLocal.jar"
	// LocalDynamoDBUri is the default local dynamodb URI.
	LocalDynamoDBUri = "http://127.0.0.1:8000"
)

// TestDynamoDBRunner manages starting/stopping a local dynamodb test server.
type TestDynamoDBRunner struct {
	cmd *exec.Cmd
}

// NewTestDynamoDBRunner instatiates a new runner.
func NewTestDynamoDBRunner() (*TestDynamoDBRunner, error) {
	tdr := new(TestDynamoDBRunner)
	if err := tdr.downloadIfNecessary(); err != nil {
		return nil, err
	}
	return tdr, nil
}

func (tdr *TestDynamoDBRunner) tmpDir() string {
	return filepath.Join(os.TempDir(), LocalDynamoDBTmpDir)
}

func (tdr *TestDynamoDBRunner) pidFilePath() string {
	return filepath.Join(tdr.tmpDir(), LocalDynamoDBPidFile)
}

func (tdr *TestDynamoDBRunner) jarFilePath() string {
	return filepath.Join(tdr.tmpDir(), LocalDynamoDBJarFile)
}

func (tdr *TestDynamoDBRunner) writePid(pid int) error {
	out, err := os.Create(tdr.pidFilePath())
	if err != nil {
		return err
	}
	_, err = out.WriteString(strconv.Itoa(pid))
	defer out.Close()
	return err
}

func (tdr *TestDynamoDBRunner) getPid() (int, error) {
	pidStr, err := ioutil.ReadFile(tdr.pidFilePath())
	if err != nil {
		return 0, err
	}
	return strconv.Atoi(string(pidStr))
}

func (tdr *TestDynamoDBRunner) downloadIfNecessary() error {
	// does the jar file exist?
	jarPath := tdr.jarFilePath()
	if _, err := os.Stat(jarPath); err == nil {
		return nil
	}

	// create the tmp directory if it doesn't exist
	if _, err := os.Stat(tdr.tmpDir()); os.IsNotExist(err) {
		if err := os.Mkdir(tdr.tmpDir(), os.ModeDir|os.ModePerm); err != nil {
			return err
		}
	}

	// download
	response, err := http.Get(LocalDynamoDBDownloadURI)
	if err != nil {
		return err
	}
	defer libkb.DiscardAndCloseBody(response)

	// read body into buffer and compute the hash
	var buf bytes.Buffer
	if _, err := buf.ReadFrom(response.Body); err != nil {
		return err
	}
	hash := sha256.New()
	hash.Write(buf.Bytes())
	sha256Hash := hex.EncodeToString(hash.Sum(nil))

	// verify the hash
	if sha256Hash != LocalDynamoDBSha256Hash {
		return fmt.Errorf("Expected hash %s, got: %s\n",
			LocalDynamoDBSha256Hash, sha256Hash)
	}

	// create the download file
	path := filepath.Join(tdr.tmpDir(), "dynamodb_local.tar.gz")
	out, err := os.Create(path)
	if err != nil {
		return err
	}
	defer out.Close()

	// write it out
	_, err = io.Copy(out, bytes.NewReader(buf.Bytes()))
	if err != nil {
		return err
	}

	// untar
	untar := exec.Command("tar", "-C", tdr.tmpDir(), "-xzvf", path)
	return untar.Run()
}

// Run starts the local DynamoDB server.
func (tdr *TestDynamoDBRunner) Run(t logger.TestLogBackend) {
	// kill any old process
	if pid, err := tdr.getPid(); err == nil {
		if p, err := os.FindProcess(pid); err == nil {
			if err := p.Kill(); err != nil {
				// you might think this would satisfy !os.IsNotExist
				// but alas, no, you'd be really wrong about this.
				if err.Error() != "os: process already finished" {
					t.Fatal(err)
				}
			}
		}
	}

	// setup the command
	tmpDir := tdr.tmpDir()
	tdr.cmd = exec.Command("java",
		"-Djava.library.path="+filepath.Join(tmpDir, "DynamoDBLocal_lib"),
		"-jar", tdr.jarFilePath(), "-inMemory")

	// exec in a goroutine
	go func() {
		// start dynamo
		if err := tdr.cmd.Start(); err != nil {
			t.Fatal(err)
		}
		if err := tdr.writePid(tdr.cmd.Process.Pid); err != nil {
			t.Fatal(err)
		}
		// wait on exit
		tdr.cmd.Wait()
	}()

	// wait for it to come up
	ok := false
	for i := 0; i < 200; i++ {
		if _, err := http.Get(LocalDynamoDBUri); err == nil {
			ok = true
			break
		}
		time.Sleep(time.Millisecond * 250)
	}
	if !ok {
		t.Fatal("dynamodb did not start up cleanly")
	}
}
<commit_msg>dynamo: update local dynamodb version used in tests<commit_after>package libkbfs

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"time"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/logger"
)

const (
	// LocalDynamoDBDownloadURI source: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Tools.DynamoDBLocal.html
	// We don't use latest because Amazon doesn't offer https downloads of this. So we peg to a revision and verify the hash.
	LocalDynamoDBDownloadURI = "http://dynamodb-local.s3-website-us-west-2.amazonaws.com/dynamodb_local_2016-04-19.tar.gz"
	// LocalDynamoDBSha256Hash is the sha256 hash of the above tar ball.
	LocalDynamoDBSha256Hash = "35bcbf97c1c3ef8607ac0032d6127eae313edd26a73c80fdc230e4d8a14c1c33"
	// LocalDynamoDBTmpDir is relative to the system's own TempDir.
	LocalDynamoDBTmpDir = "dynamodb_local"
	// LocalDynamoDBPidFile contains the process ID.
	LocalDynamoDBPidFile = "dynamodb.pid"
	// LocalDynamoDBJarFile is the name of the local dynamodb server jar file.
	LocalDynamoDBJarFile = "DynamoDBLocal.jar"
	// LocalDynamoDBUri is the default local dynamodb URI.
	LocalDynamoDBUri = "http://127.0.0.1:8000"
)

// TestDynamoDBRunner manages starting/stopping a local dynamodb test server.
type TestDynamoDBRunner struct {
	cmd *exec.Cmd
}

// NewTestDynamoDBRunner instatiates a new runner.
func NewTestDynamoDBRunner() (*TestDynamoDBRunner, error) {
	tdr := new(TestDynamoDBRunner)
	if err := tdr.downloadIfNecessary(); err != nil {
		return nil, err
	}
	return tdr, nil
}

func (tdr *TestDynamoDBRunner) tmpDir() string {
	return filepath.Join(os.TempDir(), LocalDynamoDBTmpDir)
}

func (tdr *TestDynamoDBRunner) pidFilePath() string {
	return filepath.Join(tdr.tmpDir(), LocalDynamoDBPidFile)
}

func (tdr *TestDynamoDBRunner) jarFilePath() string {
	return filepath.Join(tdr.tmpDir(), LocalDynamoDBJarFile)
}

func (tdr *TestDynamoDBRunner) writePid(pid int) error {
	out, err := os.Create(tdr.pidFilePath())
	if err != nil {
		return err
	}
	_, err = out.WriteString(strconv.Itoa(pid))
	defer out.Close()
	return err
}

func (tdr *TestDynamoDBRunner) getPid() (int, error) {
	pidStr, err := ioutil.ReadFile(tdr.pidFilePath())
	if err != nil {
		return 0, err
	}
	return strconv.Atoi(string(pidStr))
}

func (tdr *TestDynamoDBRunner) downloadIfNecessary() error {
	// does the jar file exist?
	jarPath := tdr.jarFilePath()
	if _, err := os.Stat(jarPath); err == nil {
		return nil
	}

	// create the tmp directory if it doesn't exist
	if _, err := os.Stat(tdr.tmpDir()); os.IsNotExist(err) {
		if err := os.Mkdir(tdr.tmpDir(), os.ModeDir|os.ModePerm); err != nil {
			return err
		}
	}

	// download
	response, err := http.Get(LocalDynamoDBDownloadURI)
	if err != nil {
		return err
	}
	defer libkb.DiscardAndCloseBody(response)

	// read body into buffer and compute the hash
	var buf bytes.Buffer
	if _, err := buf.ReadFrom(response.Body); err != nil {
		return err
	}
	hash := sha256.New()
	hash.Write(buf.Bytes())
	sha256Hash := hex.EncodeToString(hash.Sum(nil))

	// verify the hash
	if sha256Hash != LocalDynamoDBSha256Hash {
		return fmt.Errorf("Expected hash %s, got: %s\n",
			LocalDynamoDBSha256Hash, sha256Hash)
	}

	// create the download file
	path := filepath.Join(tdr.tmpDir(), "dynamodb_local.tar.gz")
	out, err := os.Create(path)
	if err != nil {
		return err
	}
	defer out.Close()

	// write it out
	_, err = io.Copy(out, bytes.NewReader(buf.Bytes()))
	if err != nil {
		return err
	}

	// untar
	untar := exec.Command("tar", "-C", tdr.tmpDir(), "-xzvf", path)
	return untar.Run()
}

// Run starts the local DynamoDB server.
func (tdr *TestDynamoDBRunner) Run(t logger.TestLogBackend) {
	// kill any old process
	if pid, err := tdr.getPid(); err == nil {
		if p, err := os.FindProcess(pid); err == nil {
			if err := p.Kill(); err != nil {
				// you might think this would satisfy !os.IsNotExist
				// but alas, no, you'd be really wrong about this.
				if err.Error() != "os: process already finished" {
					t.Fatal(err)
				}
			}
		}
	}

	// setup the command
	tmpDir := tdr.tmpDir()
	tdr.cmd = exec.Command("java",
		"-Djava.library.path="+filepath.Join(tmpDir, "DynamoDBLocal_lib"),
		"-jar", tdr.jarFilePath(), "-inMemory")

	// exec in a goroutine
	go func() {
		// start dynamo
		if err := tdr.cmd.Start(); err != nil {
			t.Fatal(err)
		}
		if err := tdr.writePid(tdr.cmd.Process.Pid); err != nil {
			t.Fatal(err)
		}
		// wait on exit
		tdr.cmd.Wait()
	}()

	// wait for it to come up
	ok := false
	for i := 0; i < 200; i++ {
		if _, err := http.Get(LocalDynamoDBUri); err == nil {
			ok = true
			break
		}
		time.Sleep(time.Millisecond * 250)
	}
	if !ok {
		t.Fatal("dynamodb did not start up cleanly")
	}
}
<|endoftext|>


<commit_before>// Package installer is the installer for the genesis package.
// It handles file backup, manages history, reports on progress,
// and invents higher-order tasks (such as if-then tasks)>
package installer

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path"

	"github.com/wx13/genesis"
)

// ParseFlags does all the flag parsing for the installer.
func (inst *Installer) ParseFlags() {

	// Grab the executable name for usage printout.
	execName := path.Base(os.Args[0])

	// Main help screen.
	flag.Usage = func() {
		fmt.Println("")
		fmt.Println("Usage:")
		fmt.Println("")
		fmt.Printf("  %s -h\n", execName)
		fmt.Printf("  %s (status|install|remove) [-verbose] [-tmpdir] [-dir] [-tags] [-skip-tags]\n", execName)
		fmt.Printf("  %s build [dir...]\n", execName)
		fmt.Printf("  %s rerun\n", execName)
		fmt.Println("")
		fmt.Println("Commands:")
		fmt.Println("")
		fmt.Println("  status    Show the current installation.")
		fmt.Println("  install   Run the installer.")
		fmt.Println("  remove    Reverse the installation process.")
		fmt.Println("  rerun     Start a command prompt to search/view/edit/run previous commands.")
		fmt.Println("  build     Add file resources to executable to build a stand-alone installer.")
		fmt.Println("")
		fmt.Println("For details on individual command options, run './installer <cmd> -h'.")
		fmt.Println("")
		flag.PrintDefaults()
	}

	// Options for the "run" commands: install, remove, status.
	runFlag := flag.NewFlagSet("run", flag.ExitOnError)
	verbose := runFlag.Bool("verbose", false, "Verbose")
	tmpdir := runFlag.String("tmpdir", "", "Temp directory for unpacked files; empty string == default location")
	dir := runFlag.String("dir", "~/.genesis", "Storage directory for data. Defaults to ~/.genesis")
	doTags := runFlag.String("tags", "", "Specify comma-separated tags to run.  Defaults to all.")
	skipTags := runFlag.String("skip-tags", "", "Specify comma-separated tags to skip.  Defaults to none.")
	runFlag.Usage = func() {
		fmt.Println("")
		fmt.Println("Usage:")
		fmt.Println("")
		fmt.Printf("  %s (status|install|remove) [-verbose] [-tmpdir] [-storedir] [-tags] [-skip-tags]\n", execName)
		fmt.Println("")
		fmt.Println("Options:")
		fmt.Println("")
		runFlag.PrintDefaults()
		fmt.Println("")
		fmt.Println("User options:")
		fmt.Println("")
		inst.UserFlags.PrintDefaults()
		fmt.Println("")
	}

	buildFlag := flag.NewFlagSet("build", flag.ExitOnError)
	buildFlag.Usage = func() {
		fmt.Println("")
		fmt.Println("Usage:")
		fmt.Println("")
		fmt.Printf("  %s build [list of directories]\n", execName)
		fmt.Println("")
	}
	rerunFlag := flag.NewFlagSet("rerun", flag.ExitOnError)

	// Print help screen if no arguments are given.
	if len(os.Args) <= 1 {
		flag.Usage()
		os.Exit(1)
	}

	// Get the subcommand.
	cmd := os.Args[1]

	// Parse the subcommand options.
	switch cmd {
	case "install", "remove", "status":
		runFlag.Parse(os.Args[2:])
		inst.UserFlags.Parse(os.Args[2:])
	case "build":
		buildFlag.Parse(os.Args[2:])
	case "rerun":
		rerunFlag.Parse(os.Args[2:])
	default:
		flag.Usage()
		os.Exit(1)
	}

	inst.Cmd = cmd
	inst.Verbose = *verbose
	inst.DoTags = *doTags
	inst.SkipTags = *skipTags

	inst.Tmpdir, _ = ioutil.TempDir(*tmpdir, "genesis")
	inst.Dir = genesis.ExpandHome(*dir)

}
<commit_msg>bugfix: allow errors through runflag parser, to get to userflag parser<commit_after>// Package installer is the installer for the genesis package.
// It handles file backup, manages history, reports on progress,
// and invents higher-order tasks (such as if-then tasks)>
package installer

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path"

	"github.com/wx13/genesis"
)

// ParseFlags does all the flag parsing for the installer.
func (inst *Installer) ParseFlags() {

	// Grab the executable name for usage printout.
	execName := path.Base(os.Args[0])

	// Main help screen.
	flag.Usage = func() {
		fmt.Println("")
		fmt.Println("Usage:")
		fmt.Println("")
		fmt.Printf("  %s -h\n", execName)
		fmt.Printf("  %s (status|install|remove) [-verbose] [-tmpdir] [-dir] [-tags] [-skip-tags]\n", execName)
		fmt.Printf("  %s build [dir...]\n", execName)
		fmt.Printf("  %s rerun\n", execName)
		fmt.Println("")
		fmt.Println("Commands:")
		fmt.Println("")
		fmt.Println("  status    Show the current installation.")
		fmt.Println("  install   Run the installer.")
		fmt.Println("  remove    Reverse the installation process.")
		fmt.Println("  rerun     Start a command prompt to search/view/edit/run previous commands.")
		fmt.Println("  build     Add file resources to executable to build a stand-alone installer.")
		fmt.Println("")
		fmt.Println("For details on individual command options, run './installer <cmd> -h'.")
		fmt.Println("")
		flag.PrintDefaults()
	}

	// Options for the "run" commands: install, remove, status.
	runFlag := flag.NewFlagSet("run", flag.ContinueOnError)
	verbose := runFlag.Bool("verbose", false, "Verbose")
	tmpdir := runFlag.String("tmpdir", "", "Temp directory for unpacked files; empty string == default location")
	dir := runFlag.String("dir", "~/.genesis", "Storage directory for data. Defaults to ~/.genesis")
	doTags := runFlag.String("tags", "", "Specify comma-separated tags to run.  Defaults to all.")
	skipTags := runFlag.String("skip-tags", "", "Specify comma-separated tags to skip.  Defaults to none.")
	runFlag.Usage = func() {
		fmt.Println("")
		fmt.Println("Usage:")
		fmt.Println("")
		fmt.Printf("  %s (status|install|remove) [-verbose] [-tmpdir] [-storedir] [-tags] [-skip-tags]\n", execName)
		fmt.Println("")
		fmt.Println("Options:")
		fmt.Println("")
		runFlag.PrintDefaults()
		fmt.Println("")
		fmt.Println("User options:")
		fmt.Println("")
		inst.UserFlags.PrintDefaults()
		fmt.Println("")
	}

	buildFlag := flag.NewFlagSet("build", flag.ExitOnError)
	buildFlag.Usage = func() {
		fmt.Println("")
		fmt.Println("Usage:")
		fmt.Println("")
		fmt.Printf("  %s build [list of directories]\n", execName)
		fmt.Println("")
	}
	rerunFlag := flag.NewFlagSet("rerun", flag.ExitOnError)

	// Print help screen if no arguments are given.
	if len(os.Args) <= 1 {
		flag.Usage()
		os.Exit(1)
	}

	// Get the subcommand.
	cmd := os.Args[1]

	// Parse the subcommand options.
	switch cmd {
	case "install", "remove", "status":
		runFlag.Parse(os.Args[2:])
		inst.UserFlags.Parse(os.Args[2:])
	case "build":
		buildFlag.Parse(os.Args[2:])
	case "rerun":
		rerunFlag.Parse(os.Args[2:])
	default:
		flag.Usage()
		os.Exit(1)
	}

	inst.Cmd = cmd
	inst.Verbose = *verbose
	inst.DoTags = *doTags
	inst.SkipTags = *skipTags

	inst.Tmpdir, _ = ioutil.TempDir(*tmpdir, "genesis")
	inst.Dir = genesis.ExpandHome(*dir)

}
<|endoftext|>


<commit_before>// Copyright 2017 tsuru 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 integration

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"time"

	"google.golang.org/api/container/v1"
	"google.golang.org/api/option"

	"golang.org/x/net/context"
)

const gceClusterStatusRunning = "RUNNING"

var clusterName = fmt.Sprintf("integration-test-%d", randInt())
var zone = os.Getenv("GCE_ZONE")
var projectID = os.Getenv("GCE_PROJECT_ID")
var serviceAccountFile = os.Getenv("GCE_SERVICE_ACCOUNT_FILE")

type gceClusterManager struct {
	client  *gceClient
	cluster *container.Cluster
}

func randInt() int {
	rand.Seed(time.Now().UnixNano())
	return rand.Int()
}

func (g *gceClusterManager) Name() string {
	return "gce"
}

func (g *gceClusterManager) Provisioner() string {
	return "kubernetes"
}

func (g *gceClusterManager) IP(env *Environment) string {
	g.fetchClusterData()
	if g.cluster != nil {
		return g.cluster.Endpoint
	}
	return ""
}

func (g *gceClusterManager) Start(env *Environment) *Result {
	ctx := context.Background()
	client, err := newClient(ctx, projectID, option.WithServiceAccountFile(serviceAccountFile))
	if err != nil {
		return nil
	}
	g.client = client
	g.client.createCluster(clusterName, zone, 1)
	return &Result{ExitCode: 0}
}

func (g *gceClusterManager) Delete(env *Environment) *Result {
	g.client.deleteCluster(g.cluster.Name, zone)
	return &Result{ExitCode: 0}
}

func (g *gceClusterManager) fetchClusterData() {
	if g.cluster != nil && g.cluster.Status == gceClusterStatusRunning {
		return
	}
	retries := 20
	sleepTime := 20 * time.Second
	for i := 0; i < retries; i++ {
		cluster, err := g.client.describeCluster(clusterName, zone)
		if err == nil && cluster.Status == gceClusterStatusRunning {
			g.cluster = cluster
			return
		}
		time.Sleep(sleepTime)
	}
}

func (g *gceClusterManager) credentials(env *Environment) (map[string]string, error) {
	g.fetchClusterData()
	if g.cluster == nil {
		return nil, fmt.Errorf("cluster unavailable")
	}
	credentials := make(map[string]string)
	credentials["username"] = g.cluster.MasterAuth.Username
	credentials["password"] = g.cluster.MasterAuth.Password
	contents, err := base64.StdEncoding.DecodeString(g.cluster.MasterAuth.ClusterCaCertificate)
	if err != nil {
		return credentials, err
	}
	tmpfile, err := ioutil.TempFile("", "gce-ca")
	if err != nil {
		return credentials, err
	}
	if _, err := tmpfile.Write(contents); err != nil {
		return credentials, err
	}
	if err := tmpfile.Close(); err != nil {
		return credentials, err
	}
	credentials["certificateFilename"] = tmpfile.Name()
	return credentials, nil
}

func (g *gceClusterManager) UpdateParams(env *Environment) []string {
	address := fmt.Sprintf("https://%s", g.IP(env))
	credentials, err := g.credentials(env)
	if err != nil {
		return []string{}
	}
	return []string{
		"--addr", address,
		"--custom", "username=" + credentials["username"],
		"--custom", "password=" + credentials["password"],
		"--cacert", credentials["certificateFilename"],
	}
}
<commit_msg>integration: create GCE service account file from env var<commit_after>// Copyright 2017 tsuru 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 integration

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"time"

	"google.golang.org/api/container/v1"
	"google.golang.org/api/option"

	"golang.org/x/net/context"
)

const gceClusterStatusRunning = "RUNNING"

var clusterName = fmt.Sprintf("integration-test-%d", randInt())
var zone = os.Getenv("GCE_ZONE")
var projectID = os.Getenv("GCE_PROJECT_ID")
var serviceAccount = os.Getenv("GCE_SERVICE_ACCOUNT")

type gceClusterManager struct {
	client  *gceClient
	cluster *container.Cluster
}

func randInt() int {
	rand.Seed(time.Now().UnixNano())
	return rand.Int()
}

func createTempFile(data []byte, prefix string) (string, error) {
	tmpfile, err := ioutil.TempFile("", prefix)
	if err != nil {
		return "", err
	}
	if _, err := tmpfile.Write(data); err != nil {
		return "", err
	}
	if err := tmpfile.Close(); err != nil {
		return "", err
	}
	return tmpfile.Name(), nil
}

func (g *gceClusterManager) Name() string {
	return "gce"
}

func (g *gceClusterManager) Provisioner() string {
	return "kubernetes"
}

func (g *gceClusterManager) IP(env *Environment) string {
	g.fetchClusterData()
	if g.cluster != nil {
		return g.cluster.Endpoint
	}
	return ""
}

func (g *gceClusterManager) Start(env *Environment) *Result {
	ctx := context.Background()
	serviceAccountFile, err := createTempFile([]byte(serviceAccount), "gce-sa-")
	if err != nil {
		return nil
	}
	client, err := newClient(ctx, projectID, option.WithServiceAccountFile(serviceAccountFile))
	if err != nil {
		return nil
	}
	g.client = client
	g.client.createCluster(clusterName, zone, 1)
	return &Result{ExitCode: 0}
}

func (g *gceClusterManager) Delete(env *Environment) *Result {
	g.client.deleteCluster(g.cluster.Name, zone)
	return &Result{ExitCode: 0}
}

func (g *gceClusterManager) fetchClusterData() {
	if g.cluster != nil && g.cluster.Status == gceClusterStatusRunning {
		return
	}
	retries := 20
	sleepTime := 20 * time.Second
	for i := 0; i < retries; i++ {
		cluster, err := g.client.describeCluster(clusterName, zone)
		if err == nil && cluster.Status == gceClusterStatusRunning {
			g.cluster = cluster
			return
		}
		time.Sleep(sleepTime)
	}
}

func (g *gceClusterManager) credentials(env *Environment) (map[string]string, error) {
	g.fetchClusterData()
	if g.cluster == nil {
		return nil, fmt.Errorf("cluster unavailable")
	}
	credentials := make(map[string]string)
	credentials["username"] = g.cluster.MasterAuth.Username
	credentials["password"] = g.cluster.MasterAuth.Password
	contents, err := base64.StdEncoding.DecodeString(g.cluster.MasterAuth.ClusterCaCertificate)
	if err != nil {
		return credentials, err
	}
	filename, err := createTempFile(contents, "gce-ca-")
	if err != nil {
		return credentials, err
	}
	credentials["certificateFilename"] = filename
	return credentials, nil
}

func (g *gceClusterManager) UpdateParams(env *Environment) []string {
	address := fmt.Sprintf("https://%s", g.IP(env))
	credentials, err := g.credentials(env)
	if err != nil {
		return []string{}
	}
	return []string{
		"--addr", address,
		"--custom", "username=" + credentials["username"],
		"--custom", "password=" + credentials["password"],
		"--cacert", credentials["certificateFilename"],
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// +build !appengine

package internal

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"sync"
	"time"
)

// jsonLogger writes logs in the JSON format required for Flex Logging. It can
// be used concurrently.
type jsonLogger struct {
	mu  sync.Mutex
	enc *json.Encoder
}

type logLine struct {
	Message   string       `json:"message"`
	Timestamp logTimestamp `json:"timestamp"`
	Severity  string       `json:"severity"`
	TraceID   string       `json:"traceId,omitempty"`
}

type logTimestamp struct {
	Seconds int64 `json:"seconds"`
	Nanos   int   `json:"nanos"`
}

var (
	logger     *jsonLogger
	loggerOnce sync.Once

	logPath      = "/var/log/app_engine/app.json"
	stdoutLogger = newJSONLogger(os.Stdout)
	testLogger   = newJSONLogger(ioutil.Discard)

	levels = map[int64]string{
		0: "DEBUG",
		1: "INFO",
		2: "WARNING",
		3: "ERROR",
		4: "CRITICAL",
	}
)

func globalLogger() *jsonLogger {
	loggerOnce.Do(func() {
		f, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			log.Printf("failed to open/create log file, logging to stdout: %v", err)
			logger = stdoutLogger
			return
		}

		logger = newJSONLogger(f)
	})

	return logger
}

func logf(ctx *context, level int64, format string, args ...interface{}) {
	s := strings.TrimSpace(fmt.Sprintf(format, args...))
	now := time.Now()

	trace := ctx.req.Header.Get(traceHeader)
	if i := strings.Index(trace, "/"); i > -1 {
		trace = trace[:i]
	}

	line := &logLine{
		Message: s,
		Timestamp: logTimestamp{
			Seconds: now.Unix(),
			Nanos:   now.Nanosecond(),
		},
		Severity: levels[level],
		TraceID:  trace,
	}

	if err := ctx.logger.emit(line); err != nil {
		log.Printf("failed to write log line to file: %v", err)
	}

	log.Print(levels[level] + ": " + s)
}

func newJSONLogger(w io.Writer) *jsonLogger {
	return &jsonLogger{
		enc: json.NewEncoder(w),
	}
}

func (l *jsonLogger) emit(line *logLine) error {
	l.mu.Lock()
	defer l.mu.Unlock()

	return l.enc.Encode(line)
}
<commit_msg>internal: Flex logging falls back to stderr.<commit_after>// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// +build !appengine

package internal

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"sync"
	"time"
)

// jsonLogger writes logs in the JSON format required for Flex Logging. It can
// be used concurrently.
type jsonLogger struct {
	mu  sync.Mutex
	enc *json.Encoder
}

type logLine struct {
	Message   string       `json:"message"`
	Timestamp logTimestamp `json:"timestamp"`
	Severity  string       `json:"severity"`
	TraceID   string       `json:"traceId,omitempty"`
}

type logTimestamp struct {
	Seconds int64 `json:"seconds"`
	Nanos   int   `json:"nanos"`
}

var (
	logger     *jsonLogger
	loggerOnce sync.Once

	logPath      = "/var/log/app_engine/app.json"
	stderrLogger = newJSONLogger(os.Stderr)
	testLogger   = newJSONLogger(ioutil.Discard)

	levels = map[int64]string{
		0: "DEBUG",
		1: "INFO",
		2: "WARNING",
		3: "ERROR",
		4: "CRITICAL",
	}
)

func globalLogger() *jsonLogger {
	loggerOnce.Do(func() {
		f, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			log.Printf("failed to open/create log file, logging to stderr: %v", err)
			logger = stderrLogger
			return
		}

		logger = newJSONLogger(f)
	})

	return logger
}

func logf(ctx *context, level int64, format string, args ...interface{}) {
	s := strings.TrimSpace(fmt.Sprintf(format, args...))
	now := time.Now()

	trace := ctx.req.Header.Get(traceHeader)
	if i := strings.Index(trace, "/"); i > -1 {
		trace = trace[:i]
	}

	line := &logLine{
		Message: s,
		Timestamp: logTimestamp{
			Seconds: now.Unix(),
			Nanos:   now.Nanosecond(),
		},
		Severity: levels[level],
		TraceID:  trace,
	}

	if err := ctx.logger.emit(line); err != nil {
		log.Printf("failed to write log line to file: %v", err)
	}

	log.Print(levels[level] + ": " + s)
}

func newJSONLogger(w io.Writer) *jsonLogger {
	return &jsonLogger{
		enc: json.NewEncoder(w),
	}
}

func (l *jsonLogger) emit(line *logLine) error {
	l.mu.Lock()
	defer l.mu.Unlock()

	return l.enc.Encode(line)
}
<|endoftext|>


<commit_before>package cmd

import (
	"os"
	"path/filepath"

	"fmt"

	"github.com/go-errors/errors"
	"github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/internal/fs"
	"github.com/spf13/cobra"
)

// verifyCmd represents the verify command
var verifyCmd = &cobra.Command{
	Use:   "verify [irma_configuration]",
	Short: "Verify irma_configuration folder correctness and authenticity",
	Long:  `The verify command parses the specified irma_configuration directory, or the current directory if not specified, and checks the signatures of the contained scheme managers.`,
	Args:  cobra.MaximumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		var err error
		var path string
		if len(args) > 0 {
			path = args[0]
		} else {
			path, err = os.Getwd()
			if err != nil {
				return err
			}
		}
		if err = RunVerify(path, true); err == nil {
			fmt.Println()
			fmt.Println("Verification was successful.")
		} else {
			die("Verification failed", err)
		}
		return nil
	},
}

func RunVerify(path string, verbose bool) error {
	path, err := filepath.Abs(path)
	if err != nil {
		return err
	}

	isScheme, err := fs.PathExists(filepath.Join(path, "index"))
	if err != nil {
		return err
	}
	if !isScheme {
		if verbose {
			fmt.Println("No index file found; verifying subdirectories")
		}
		return VerifyIrmaConfiguration(path)
	} else {
		if verbose {
			fmt.Println("Verifying scheme " + filepath.Base(path))
		}
		return VerifyScheme(path)
	}
}

func VerifyScheme(path string) error {
	conf, err := irma.NewConfigurationReadOnly(filepath.Dir(path))
	if err != nil {
		return err
	}

	scheme := irma.NewSchemeManager(filepath.Base(path))
	if err = conf.ParseSchemeManagerFolder(path, scheme); err != nil {
		return err
	}

	if err := conf.CheckKeys(); err != nil {
		return err
	}

	if err := conf.VerifySchemeManager(scheme); err != nil {
		return err
	}

	for _, warning := range conf.Warnings {
		fmt.Println("Warning: " + warning)
	}
	return nil
}

func VerifyIrmaConfiguration(path string) error {
	if filepath.Base(path) != "irma_configuration" {
		fmt.Printf("Notice: specified folder name is '%s'; when using in IRMA applications it should be called 'irma_configuration'\n", filepath.Base(path))
	}

	conf, err := irma.NewConfigurationReadOnly(path)
	if err != nil {
		return err
	}
	if err := conf.ParseFolder(); err != nil {
		return err
	}
	if err := conf.CheckKeys(); err != nil {
		return err
	}
	if len(conf.SchemeManagers) == 0 {
		return errors.New("Specified folder doesn't contain any schemes")
	}

	for _, manager := range conf.SchemeManagers {
		if err := conf.VerifySchemeManager(manager); err != nil {
			return err
		}
	}

	for _, warning := range conf.Warnings {
		fmt.Println("Warning: " + warning)
	}

	return nil
}

func init() {
	schemeCmd.AddCommand(verifyCmd)
}
<commit_msg>Drop warning about irma_configuration scheme folder name<commit_after>package cmd

import (
	"os"
	"path/filepath"

	"fmt"

	"github.com/go-errors/errors"
	"github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/internal/fs"
	"github.com/spf13/cobra"
)

// verifyCmd represents the verify command
var verifyCmd = &cobra.Command{
	Use:   "verify [irma_configuration]",
	Short: "Verify irma_configuration folder correctness and authenticity",
	Long:  `The verify command parses the specified irma_configuration directory, or the current directory if not specified, and checks the signatures of the contained scheme managers.`,
	Args:  cobra.MaximumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		var err error
		var path string
		if len(args) > 0 {
			path = args[0]
		} else {
			path, err = os.Getwd()
			if err != nil {
				return err
			}
		}
		if err = RunVerify(path, true); err == nil {
			fmt.Println()
			fmt.Println("Verification was successful.")
		} else {
			die("Verification failed", err)
		}
		return nil
	},
}

func RunVerify(path string, verbose bool) error {
	path, err := filepath.Abs(path)
	if err != nil {
		return err
	}

	isScheme, err := fs.PathExists(filepath.Join(path, "index"))
	if err != nil {
		return err
	}
	if !isScheme {
		if verbose {
			fmt.Println("No index file found; verifying subdirectories")
		}
		return VerifyIrmaConfiguration(path)
	} else {
		if verbose {
			fmt.Println("Verifying scheme " + filepath.Base(path))
		}
		return VerifyScheme(path)
	}
}

func VerifyScheme(path string) error {
	conf, err := irma.NewConfigurationReadOnly(filepath.Dir(path))
	if err != nil {
		return err
	}

	scheme := irma.NewSchemeManager(filepath.Base(path))
	if err = conf.ParseSchemeManagerFolder(path, scheme); err != nil {
		return err
	}

	if err := conf.CheckKeys(); err != nil {
		return err
	}

	if err := conf.VerifySchemeManager(scheme); err != nil {
		return err
	}

	for _, warning := range conf.Warnings {
		fmt.Println("Warning: " + warning)
	}
	return nil
}

func VerifyIrmaConfiguration(path string) error {
	conf, err := irma.NewConfigurationReadOnly(path)
	if err != nil {
		return err
	}
	if err := conf.ParseFolder(); err != nil {
		return err
	}
	if err := conf.CheckKeys(); err != nil {
		return err
	}
	if len(conf.SchemeManagers) == 0 {
		return errors.New("Specified folder doesn't contain any schemes")
	}

	for _, manager := range conf.SchemeManagers {
		if err := conf.VerifySchemeManager(manager); err != nil {
			return err
		}
	}

	for _, warning := range conf.Warnings {
		fmt.Println("Warning: " + warning)
	}

	return nil
}

func init() {
	schemeCmd.AddCommand(verifyCmd)
}
<|endoftext|>


<commit_before>package ironman

import (
	"bytes"
	"context"
	"io"

	"log"
	"os"
	"path/filepath"
	gtemplate "text/template"

	"github.com/ironman-project/ironman/template"
	"github.com/ironman-project/ironman/template/values"

	"github.com/ironman-project/ironman/template/validator"

	"github.com/blevesearch/bleve"
	"github.com/ironman-project/ironman/template/manager"
	"github.com/ironman-project/ironman/template/manager/git"
	"github.com/ironman-project/ironman/template/model"
	"github.com/ironman-project/ironman/template/repository"
	brepository "github.com/ironman-project/ironman/template/repository/bleve"
	"github.com/pkg/errors"
)

const (
	indexName          = "templates.index"
	templatesDirectory = "templates"
)

const validatoinTemplateText = ``

//Ironman is the one administering the local
type Ironman struct {
	manager                manager.Manager
	modelReader            model.Reader
	repository             repository.Repository
	home                   string
	validators             []validator.Validator
	logger                 *log.Logger
	output                 io.Writer
	validationTempl        *gtemplate.Template
	validationTemplateText string
}

//New returns a new instance of ironman
func New(home string, options ...Option) *Ironman {
	logger := log.New(os.Stdout, "", 0)

	ir := &Ironman{home: home, logger: logger, output: os.Stdout}

	for _, option := range options {
		option(ir)
	}

	var err error
	ir.validationTempl, err = gtemplate.New("validationTemplate").Parse(validatoinTemplateText)
	if err != nil {
		ir.logger.Fatalf("Failed to initialize validation errors template %s", err)
	}

	if ir.manager == nil {
		manager := git.New(home, templatesDirectory, git.SetOutput(ir.output))
		ir.manager = manager
	}

	if ir.repository == nil {
		indexPath := filepath.Join(home, indexName)
		index, err := buildIndex(indexPath)
		if err != nil {
			ir.logger.Fatal("Failed to create ironman templates index", err)
		}
		ir.repository = brepository.New(
			brepository.SetIndex(index),
		)
	}

	if ir.modelReader == nil {
		decoder := model.NewDecoder(model.DecoderTypeYAML)
		modelReader := model.NewFSReader([]string{".git"}, model.MetadataFileExtensionYAML, decoder)
		ir.modelReader = modelReader
	}

	if ir.validators == nil {
		ir.validators = []validator.Validator{}
	}

	return ir
}

func buildIndex(path string) (bleve.Index, error) {
	// open the index
	index, err := bleve.Open(path)
	if err == bleve.ErrorIndexPathDoesNotExist {
		index, err = brepository.BuildIndex(path)
		if err != nil {
			return nil, err
		}
	} else if err != nil {
		return nil, err
	}
	return index, nil
}

//Install installs a new template based on a template locator
func (i *Ironman) Install(templateLocator string) error {

	ID, err := i.manager.Install(templateLocator)

	if err != nil {
		return err
	}

	templatePath := i.manager.TemplateLocation(ID)

	model, err := i.modelReader.Read(templatePath)

	if err != nil {
		return err
	}

	//validate model
	for _, validator := range i.validators {
		valid, validationErr, err := validator.Validate(model)

		if err != nil {
			return errors.Wrap(err, "Failed to validate model")
		}

		if !valid {
			var validationErrBuffer bytes.Buffer
			err := i.validationTempl.Execute(&validationErrBuffer, validationErr)

			if err != nil {
				return errors.Wrap(err, "Failed to create validation error message")
			}

			return errors.New(validationErrBuffer.String())
		}
	}

	_, err = i.repository.Index(model)

	if err != nil {
		//rollback manager installation
		_ = i.manager.Uninstall(ID)
		return err
	}

	return nil
}

//Link Creates a symlink to the ironman repository from any path in the filesystem
func (i *Ironman) Link(templatePath, templateID string) error {

	linkPath, err := i.manager.Link(templatePath, templateID)

	if err != nil {
		return err
	}

	model, err := i.modelReader.Read(linkPath)

	if err != nil {
		_ = i.manager.Unlink(templateID)
		return err
	}

	model.ID = templateID

	_, err = i.repository.Index(model)

	if err != nil {
		_ = i.manager.Unlink(templateID)
		return err
	}

	return nil
}

//List returns a list of all the installed ironman templates
func (i *Ironman) List() ([]*model.Template, error) {
	results, err := i.repository.List()
	if err != nil {
		return nil, err
	}

	return results, nil
}

//Uninstall uninstalls an ironman template
func (i *Ironman) Uninstall(templateID string) error {

	exists, err := i.repository.Exists(templateID)

	if err != nil {
		return errors.Wrapf(err, "Failed to validate if template exists %s", templateID)
	}

	if !exists {
		return errors.Errorf("Template is not installed")
	}

	err = i.manager.Uninstall(templateID)

	if err != nil {
		return err
	}

	_, err = i.repository.Delete(templateID)

	if err != nil {
		return err
	}

	return nil
}

//Unlink unlinks a previously linked ironman template
func (i *Ironman) Unlink(templateID string) error {

	err := i.manager.Unlink(templateID)

	if err != nil {
		return err
	}

	_, err = i.repository.Delete(templateID)

	if err != nil {
		return err
	}

	return nil
}

//Update updates an iroman template
func (i *Ironman) Update(templateID string) error {
	exists, err := i.repository.Exists(templateID)

	if err != nil {
		return errors.Wrapf(err, "Failed to validate if template exists %s", templateID)
	}

	if !exists {
		return errors.Errorf("Template is not installed")
	}

	err = i.manager.Update(templateID)

	if err != nil {
		return err
	}

	return nil
}

//Generate generates a new file or directory based on a generator
func (i *Ironman) Generate(context context.Context, templateID string, generatorID string, generationPath string, values values.Values, force bool) error {
	//First validate if template exists
	exists, err := i.repository.Exists(templateID)

	if err != nil {
		return errors.Wrapf(err, "Failed to validate if template exists %s", templateID)
	}

	if !exists {
		return errors.Errorf("Template is not installed")
	}

	templateModel, err := i.repository.FindTemplateByID(templateID)

	if err != nil {
		return errors.Wrapf(err, "Could not find template by ID %s", templateID)
	}

	genteratorModel := templateModel.Generator(generatorID)

	if genteratorModel == nil {
		return errors.Errorf("Generator %s does not exists", generatorID)
	}

	if genteratorModel.TType == model.GeneratorTypeFile {

		fileName := filepath.Base(generationPath)
		baseDir := filepath.Dir(generationPath)
		filePath := filepath.Join(baseDir, genteratorModel.FileTypeOptions.FileGenerationRelativePath, fileName)

		if _, err := os.Stat(filePath); err == nil && !force {
			return errors.Errorf("File already exists %s ", filePath)
		}

	} else {
		//If template exists validate generation directory
		err = os.Mkdir(generationPath, os.ModePerm)

		if os.IsPermission(err) {
			return errors.Wrapf(err, "Failed to create generation path %s", generationPath)
		} else if os.IsExist(err) && !force {
			empty, err := isDirEmpty(generationPath)

			if err != nil {
				return errors.Wrapf(err, "Failed to validate if generation path is empty", err)
			}

			if !empty {
				return errors.Errorf("Generation path is not empty %s", generationPath)
			}

		}
	}

	generatorPath := filepath.Join(i.home, templatesDirectory, templateModel.DirectoryName, genteratorModel.DirectoryName)

	data := template.GeneratorData{
		Template:  templateModel,
		Generator: genteratorModel,
		Values:    values,
	}

	generator := template.NewGenerator(
		generatorPath,
		generationPath,
		data,
		template.SetGeneratorOutput(i.output),
	)

	if err := generator.Generate(context); err != nil {
		return err
	}

	return nil
}

func isDirEmpty(name string) (bool, error) {
	f, err := os.Open(name)
	if err != nil {
		return false, err
	}
	defer f.Close()

	_, err = f.Readdirnames(1) // Or f.Readdir(1)
	if err == io.EOF {
		return true, nil
	}
	return false, err // Either not empty or error, suits both cases
}

//InitIronmanHome inits the ironman home directory
func InitIronmanHome(ironmanHome string) error {
	if _, err := os.Stat(ironmanHome); os.IsNotExist(err) {
		err := os.Mkdir(ironmanHome, os.ModePerm)
		if err != nil {
			return errors.Wrap(err, "Failed to initialize ironman home")
		}

		err = os.Mkdir(filepath.Join(ironmanHome, templatesDirectory), os.ModePerm)

		if err != nil {
			return errors.Wrap(err, "Failed to initialize ironman home")
		}
	}
	return nil
}
<commit_msg>validate if directory don't exists for file generator<commit_after>package ironman

import (
	"bytes"
	"context"
	"io"

	"log"
	"os"
	"path/filepath"
	gtemplate "text/template"

	"github.com/ironman-project/ironman/template"
	"github.com/ironman-project/ironman/template/values"

	"github.com/ironman-project/ironman/template/validator"

	"github.com/blevesearch/bleve"
	"github.com/ironman-project/ironman/template/manager"
	"github.com/ironman-project/ironman/template/manager/git"
	"github.com/ironman-project/ironman/template/model"
	"github.com/ironman-project/ironman/template/repository"
	brepository "github.com/ironman-project/ironman/template/repository/bleve"
	"github.com/pkg/errors"
)

const (
	indexName          = "templates.index"
	templatesDirectory = "templates"
)

const validatoinTemplateText = ``

//Ironman is the one administering the local
type Ironman struct {
	manager                manager.Manager
	modelReader            model.Reader
	repository             repository.Repository
	home                   string
	validators             []validator.Validator
	logger                 *log.Logger
	output                 io.Writer
	validationTempl        *gtemplate.Template
	validationTemplateText string
}

//New returns a new instance of ironman
func New(home string, options ...Option) *Ironman {
	logger := log.New(os.Stdout, "", 0)

	ir := &Ironman{home: home, logger: logger, output: os.Stdout}

	for _, option := range options {
		option(ir)
	}

	var err error
	ir.validationTempl, err = gtemplate.New("validationTemplate").Parse(validatoinTemplateText)
	if err != nil {
		ir.logger.Fatalf("Failed to initialize validation errors template %s", err)
	}

	if ir.manager == nil {
		manager := git.New(home, templatesDirectory, git.SetOutput(ir.output))
		ir.manager = manager
	}

	if ir.repository == nil {
		indexPath := filepath.Join(home, indexName)
		index, err := buildIndex(indexPath)
		if err != nil {
			ir.logger.Fatal("Failed to create ironman templates index", err)
		}
		ir.repository = brepository.New(
			brepository.SetIndex(index),
		)
	}

	if ir.modelReader == nil {
		decoder := model.NewDecoder(model.DecoderTypeYAML)
		modelReader := model.NewFSReader([]string{".git"}, model.MetadataFileExtensionYAML, decoder)
		ir.modelReader = modelReader
	}

	if ir.validators == nil {
		ir.validators = []validator.Validator{}
	}

	return ir
}

func buildIndex(path string) (bleve.Index, error) {
	// open the index
	index, err := bleve.Open(path)
	if err == bleve.ErrorIndexPathDoesNotExist {
		index, err = brepository.BuildIndex(path)
		if err != nil {
			return nil, err
		}
	} else if err != nil {
		return nil, err
	}
	return index, nil
}

//Install installs a new template based on a template locator
func (i *Ironman) Install(templateLocator string) error {

	ID, err := i.manager.Install(templateLocator)

	if err != nil {
		return err
	}

	templatePath := i.manager.TemplateLocation(ID)

	model, err := i.modelReader.Read(templatePath)

	if err != nil {
		return err
	}

	//validate model
	for _, validator := range i.validators {
		valid, validationErr, err := validator.Validate(model)

		if err != nil {
			return errors.Wrap(err, "Failed to validate model")
		}

		if !valid {
			var validationErrBuffer bytes.Buffer
			err := i.validationTempl.Execute(&validationErrBuffer, validationErr)

			if err != nil {
				return errors.Wrap(err, "Failed to create validation error message")
			}

			return errors.New(validationErrBuffer.String())
		}
	}

	_, err = i.repository.Index(model)

	if err != nil {
		//rollback manager installation
		_ = i.manager.Uninstall(ID)
		return err
	}

	return nil
}

//Link Creates a symlink to the ironman repository from any path in the filesystem
func (i *Ironman) Link(templatePath, templateID string) error {

	linkPath, err := i.manager.Link(templatePath, templateID)

	if err != nil {
		return err
	}

	model, err := i.modelReader.Read(linkPath)

	if err != nil {
		_ = i.manager.Unlink(templateID)
		return err
	}

	model.ID = templateID

	_, err = i.repository.Index(model)

	if err != nil {
		_ = i.manager.Unlink(templateID)
		return err
	}

	return nil
}

//List returns a list of all the installed ironman templates
func (i *Ironman) List() ([]*model.Template, error) {
	results, err := i.repository.List()
	if err != nil {
		return nil, err
	}

	return results, nil
}

//Uninstall uninstalls an ironman template
func (i *Ironman) Uninstall(templateID string) error {

	exists, err := i.repository.Exists(templateID)

	if err != nil {
		return errors.Wrapf(err, "Failed to validate if template exists %s", templateID)
	}

	if !exists {
		return errors.Errorf("Template is not installed")
	}

	err = i.manager.Uninstall(templateID)

	if err != nil {
		return err
	}

	_, err = i.repository.Delete(templateID)

	if err != nil {
		return err
	}

	return nil
}

//Unlink unlinks a previously linked ironman template
func (i *Ironman) Unlink(templateID string) error {

	err := i.manager.Unlink(templateID)

	if err != nil {
		return err
	}

	_, err = i.repository.Delete(templateID)

	if err != nil {
		return err
	}

	return nil
}

//Update updates an iroman template
func (i *Ironman) Update(templateID string) error {
	exists, err := i.repository.Exists(templateID)

	if err != nil {
		return errors.Wrapf(err, "Failed to validate if template exists %s", templateID)
	}

	if !exists {
		return errors.Errorf("Template is not installed")
	}

	err = i.manager.Update(templateID)

	if err != nil {
		return err
	}

	return nil
}

//Generate generates a new file or directory based on a generator
func (i *Ironman) Generate(context context.Context, templateID string, generatorID string, generationPath string, values values.Values, force bool) error {
	//First validate if template exists
	exists, err := i.repository.Exists(templateID)

	if err != nil {
		return errors.Wrapf(err, "Failed to validate if template exists %s", templateID)
	}

	if !exists {
		return errors.Errorf("Template is not installed")
	}

	templateModel, err := i.repository.FindTemplateByID(templateID)

	if err != nil {
		return errors.Wrapf(err, "Could not find template by ID %s", templateID)
	}

	genteratorModel := templateModel.Generator(generatorID)

	if genteratorModel == nil {
		return errors.Errorf("Generator %s does not exists", generatorID)
	}

	absGenerationPath, err := filepath.Abs(generationPath)

	if err != nil {
		return errors.Wrapf(err, "Failed to get absolute path for generation path %s", generationPath)
	}

	if genteratorModel.TType == model.GeneratorTypeFile {

		baseDir := filepath.Dir(absGenerationPath)

		if _, err := os.Stat(baseDir); os.IsNotExist(err) {
			return errors.Errorf("directory %s does not exists", filepath.Dir(generationPath))
		}

		fileName := filepath.Base(absGenerationPath)
		filePath := filepath.Join(baseDir, genteratorModel.FileTypeOptions.FileGenerationRelativePath, fileName)

		if _, err := os.Stat(filePath); err == nil && !force {
			return errors.Errorf("File already exists %s ", filePath)
		}

	} else {
		//If template exists validate generation directory
		err = os.Mkdir(absGenerationPath, os.ModePerm)

		if os.IsPermission(err) {
			return errors.Wrapf(err, "Failed to create generation path %s", absGenerationPath)
		} else if os.IsExist(err) && !force {
			empty, err := isDirEmpty(absGenerationPath)

			if err != nil {
				return errors.Wrapf(err, "Failed to validate if generation path is empty", err)
			}

			if !empty {
				return errors.Errorf("Generation path is not empty %s", absGenerationPath)
			}

		}
	}

	generatorPath := filepath.Join(i.home, templatesDirectory, templateModel.DirectoryName, genteratorModel.DirectoryName)

	data := template.GeneratorData{
		Template:  templateModel,
		Generator: genteratorModel,
		Values:    values,
	}

	generator := template.NewGenerator(
		generatorPath,
		absGenerationPath,
		data,
		template.SetGeneratorOutput(i.output),
	)

	if err := generator.Generate(context); err != nil {
		return err
	}

	return nil
}

func isDirEmpty(name string) (bool, error) {
	f, err := os.Open(name)
	if err != nil {
		return false, err
	}
	defer f.Close()

	_, err = f.Readdirnames(1) // Or f.Readdir(1)
	if err == io.EOF {
		return true, nil
	}
	return false, err // Either not empty or error, suits both cases
}

//InitIronmanHome inits the ironman home directory
func InitIronmanHome(ironmanHome string) error {
	if _, err := os.Stat(ironmanHome); os.IsNotExist(err) {
		err := os.Mkdir(ironmanHome, os.ModePerm)
		if err != nil {
			return errors.Wrap(err, "Failed to initialize ironman home")
		}

		err = os.Mkdir(filepath.Join(ironmanHome, templatesDirectory), os.ModePerm)

		if err != nil {
			return errors.Wrap(err, "Failed to initialize ironman home")
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package isolated

import (
	. "code.cloudfoundry.org/cli/cf/util/testhelpers/matchers"
	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = FDescribe("revision command", func() {
	Describe("help", func() {
		When("--help flag is set", func() {
			It("appears in cf help -a", func() {
				session := helpers.CF("help", "-a")
				Eventually(session).Should(Exit(0))
				Expect(session).To(HaveCommandInCategoryWithDescription("revision", "APPS", "Show details for a specific app revision"))
			})

			It("Displays revision command usage to output", func() {
				session := helpers.CF("revision", "--help")

				Eventually(session).Should(Exit(0))

				Expect(session).To(Say("NAME:"))
				Expect(session).To(Say("revision - Show details for a specific app revision"))
				Expect(session).To(Say("USAGE:"))
				Expect(session).To(Say(`cf revision APP_NAME [--version VERSION]`))
				Expect(session).To(Say("OPTIONS:"))
				Expect(session).To(Say("--version      The integer representing the specific revision to show"))
				Expect(session).To(Say("SEE ALSO:"))
				Expect(session).To(Say("revisions, rollback"))
			})
		})
	})
})
<commit_msg>Remove accidental FDescribe<commit_after>package isolated

import (
	. "code.cloudfoundry.org/cli/cf/util/testhelpers/matchers"
	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("revision command", func() {
	Describe("help", func() {
		When("--help flag is set", func() {
			It("appears in cf help -a", func() {
				session := helpers.CF("help", "-a")
				Eventually(session).Should(Exit(0))
				Expect(session).To(HaveCommandInCategoryWithDescription("revision", "APPS", "Show details for a specific app revision"))
			})

			It("Displays revision command usage to output", func() {
				session := helpers.CF("revision", "--help")

				Eventually(session).Should(Exit(0))

				Expect(session).To(Say("NAME:"))
				Expect(session).To(Say("revision - Show details for a specific app revision"))
				Expect(session).To(Say("USAGE:"))
				Expect(session).To(Say(`cf revision APP_NAME [--version VERSION]`))
				Expect(session).To(Say("OPTIONS:"))
				Expect(session).To(Say("--version      The integer representing the specific revision to show"))
				Expect(session).To(Say("SEE ALSO:"))
				Expect(session).To(Say("revisions, rollback"))
			})
		})
	})
})
<|endoftext|>


<commit_before>package accessverifier

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"path"
	"testing"

	"github.com/stretchr/testify/require"

	pb "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
	"gitlab.com/gitlab-org/gitlab-shell/internal/command/commandargs"
	"gitlab.com/gitlab-org/gitlab-shell/internal/config"
	"gitlab.com/gitlab-org/gitlab-shell/internal/gitlabnet"
	"gitlab.com/gitlab-org/gitlab-shell/internal/gitlabnet/testserver"
	"gitlab.com/gitlab-org/gitlab-shell/internal/testhelper"
)

var (
	repo              = "group/private"
	receivePackAction = commandargs.ReceivePack
)

func buildExpectedResponse(who string) *Response {
	response := &Response{
		Success:          true,
		UserId:           "user-1",
		Repo:             "project-26",
		Username:         "root",
		GitConfigOptions: []string{"option"},
		Gitaly: Gitaly{
			Repo: pb.Repository{
				StorageName:                   "default",
				RelativePath:                  "@hashed/5f/9c/5f9c4ab08cac7457e9111a30e4664920607ea2c115a1433d7be98e97e64244ca.git",
				GitObjectDirectory:            "path/to/git_object_directory",
				GitAlternateObjectDirectories: []string{"path/to/git_alternate_object_directory"},
				GlRepository:                  "project-26",
				GlProjectPath:                 repo,
			},
			Address: "unix:gitaly.socket",
			Token:   "token",
		},
		GitProtocol:     "protocol",
		Payload:         CustomPayload{},
		ConsoleMessages: []string{"console", "message"},
		Who:             who,
		StatusCode:      200,
	}

	return response
}

func TestSuccessfulResponses(t *testing.T) {
	client, cleanup := setup(t)
	defer cleanup()

	testCases := []struct {
		desc string
		args *commandargs.Shell
		who  string
	}{
		{
			desc: "Provide key id within the request",
			args: &commandargs.Shell{GitlabKeyId: "1"},
			who:  "key-1",
		}, {
			desc: "Provide username within the request",
			args: &commandargs.Shell{GitlabUsername: "first"},
			who:  "user-1",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.desc, func(t *testing.T) {
			result, err := client.Verify(tc.args, receivePackAction, repo)
			require.NoError(t, err)

			response := buildExpectedResponse(tc.who)
			require.Equal(t, response, result)
		})
	}
}

func TestGetCustomAction(t *testing.T) {
	client, cleanup := setup(t)
	defer cleanup()

	args := &commandargs.Shell{GitlabUsername: "custom"}
	result, err := client.Verify(args, receivePackAction, repo)
	require.NoError(t, err)

	response := buildExpectedResponse("user-1")
	response.Payload = CustomPayload{
		Action: "geo_proxy_to_primary",
		Data: CustomPayloadData{
			ApiEndpoints: []string{"geo/proxy_git_push_ssh/info_refs", "geo/proxy_git_push_ssh/push"},
			Username:     "custom",
			PrimaryRepo:  "https://repo/path",
		},
	}
	response.StatusCode = 300

	require.True(t, response.IsCustomAction())
	require.Equal(t, response, result)
}

func TestErrorResponses(t *testing.T) {
	client, cleanup := setup(t)
	defer cleanup()

	testCases := []struct {
		desc          string
		fakeId        string
		expectedError string
	}{
		{
			desc:          "A response with an error message",
			fakeId:        "2",
			expectedError: "Not allowed!",
		},
		{
			desc:          "A response with bad JSON",
			fakeId:        "3",
			expectedError: "Parsing failed",
		},
		{
			desc:          "An error response without message",
			fakeId:        "4",
			expectedError: "Internal API error (403)",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.desc, func(t *testing.T) {
			args := &commandargs.Shell{GitlabKeyId: tc.fakeId}
			resp, err := client.Verify(args, receivePackAction, repo)

			require.EqualError(t, err, tc.expectedError)
			require.Nil(t, resp)
		})
	}
}

func setup(t *testing.T) (*Client, func()) {
	testDirCleanup, err := testhelper.PrepareTestRootDir()
	require.NoError(t, err)
	defer testDirCleanup()

	body, err := ioutil.ReadFile(path.Join(testhelper.TestRoot, "responses/allowed.json"))
	require.NoError(t, err)

	allowedWithPayloadPath := path.Join(testhelper.TestRoot, "responses/allowed_with_push_payload.json")
	bodyWithPayload, err := ioutil.ReadFile(allowedWithPayloadPath)
	require.NoError(t, err)

	requests := []testserver.TestRequestHandler{
		{
			Path: "/api/v4/internal/allowed",
			Handler: func(w http.ResponseWriter, r *http.Request) {
				b, err := ioutil.ReadAll(r.Body)
				require.NoError(t, err)

				var requestBody *Request
				require.NoError(t, json.Unmarshal(b, &requestBody))

				switch requestBody.Username {
				case "first":
					_, err = w.Write(body)
					require.NoError(t, err)
				case "second":
					errBody := map[string]interface{}{
						"status":  false,
						"message": "missing user",
					}
					require.NoError(t, json.NewEncoder(w).Encode(errBody))
				case "custom":
					w.WriteHeader(http.StatusMultipleChoices)
					_, err = w.Write(bodyWithPayload)
					require.NoError(t, err)
				}

				switch requestBody.KeyId {
				case "1":
					_, err = w.Write(body)
					require.NoError(t, err)
				case "2":
					w.WriteHeader(http.StatusForbidden)
					errBody := &gitlabnet.ErrorResponse{
						Message: "Not allowed!",
					}
					require.NoError(t, json.NewEncoder(w).Encode(errBody))
				case "3":
					w.Write([]byte("{ \"message\": \"broken json!\""))
				case "4":
					w.WriteHeader(http.StatusForbidden)
				}
			},
		},
	}

	url, cleanup := testserver.StartSocketHttpServer(t, requests)

	client, err := NewClient(&config.Config{GitlabUrl: url})
	require.NoError(t, err)

	return client, cleanup
}
<commit_msg>Rename Geo Push custom action test<commit_after>package accessverifier

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"path"
	"testing"

	"github.com/stretchr/testify/require"

	pb "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
	"gitlab.com/gitlab-org/gitlab-shell/internal/command/commandargs"
	"gitlab.com/gitlab-org/gitlab-shell/internal/config"
	"gitlab.com/gitlab-org/gitlab-shell/internal/gitlabnet"
	"gitlab.com/gitlab-org/gitlab-shell/internal/gitlabnet/testserver"
	"gitlab.com/gitlab-org/gitlab-shell/internal/testhelper"
)

var (
	repo              = "group/private"
	receivePackAction = commandargs.ReceivePack
)

func buildExpectedResponse(who string) *Response {
	response := &Response{
		Success:          true,
		UserId:           "user-1",
		Repo:             "project-26",
		Username:         "root",
		GitConfigOptions: []string{"option"},
		Gitaly: Gitaly{
			Repo: pb.Repository{
				StorageName:                   "default",
				RelativePath:                  "@hashed/5f/9c/5f9c4ab08cac7457e9111a30e4664920607ea2c115a1433d7be98e97e64244ca.git",
				GitObjectDirectory:            "path/to/git_object_directory",
				GitAlternateObjectDirectories: []string{"path/to/git_alternate_object_directory"},
				GlRepository:                  "project-26",
				GlProjectPath:                 repo,
			},
			Address: "unix:gitaly.socket",
			Token:   "token",
		},
		GitProtocol:     "protocol",
		Payload:         CustomPayload{},
		ConsoleMessages: []string{"console", "message"},
		Who:             who,
		StatusCode:      200,
	}

	return response
}

func TestSuccessfulResponses(t *testing.T) {
	client, cleanup := setup(t)
	defer cleanup()

	testCases := []struct {
		desc string
		args *commandargs.Shell
		who  string
	}{
		{
			desc: "Provide key id within the request",
			args: &commandargs.Shell{GitlabKeyId: "1"},
			who:  "key-1",
		}, {
			desc: "Provide username within the request",
			args: &commandargs.Shell{GitlabUsername: "first"},
			who:  "user-1",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.desc, func(t *testing.T) {
			result, err := client.Verify(tc.args, receivePackAction, repo)
			require.NoError(t, err)

			response := buildExpectedResponse(tc.who)
			require.Equal(t, response, result)
		})
	}
}

	client, cleanup := setup(t)
func TestGeoPushGetCustomAction(t *testing.T) {
	defer cleanup()

	args := &commandargs.Shell{GitlabUsername: "custom"}
	result, err := client.Verify(args, receivePackAction, repo)
	require.NoError(t, err)

	response := buildExpectedResponse("user-1")
	response.Payload = CustomPayload{
		Action: "geo_proxy_to_primary",
		Data: CustomPayloadData{
			ApiEndpoints: []string{"geo/proxy_git_push_ssh/info_refs", "geo/proxy_git_push_ssh/push"},
			Username:     "custom",
			PrimaryRepo:  "https://repo/path",
		},
	}
	response.StatusCode = 300

	require.True(t, response.IsCustomAction())
	require.Equal(t, response, result)
}

func TestErrorResponses(t *testing.T) {
	client, cleanup := setup(t)
	defer cleanup()

	testCases := []struct {
		desc          string
		fakeId        string
		expectedError string
	}{
		{
			desc:          "A response with an error message",
			fakeId:        "2",
			expectedError: "Not allowed!",
		},
		{
			desc:          "A response with bad JSON",
			fakeId:        "3",
			expectedError: "Parsing failed",
		},
		{
			desc:          "An error response without message",
			fakeId:        "4",
			expectedError: "Internal API error (403)",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.desc, func(t *testing.T) {
			args := &commandargs.Shell{GitlabKeyId: tc.fakeId}
			resp, err := client.Verify(args, receivePackAction, repo)

			require.EqualError(t, err, tc.expectedError)
			require.Nil(t, resp)
		})
	}
}

func setup(t *testing.T) (*Client, func()) {
	testDirCleanup, err := testhelper.PrepareTestRootDir()
	require.NoError(t, err)
	defer testDirCleanup()

	body, err := ioutil.ReadFile(path.Join(testhelper.TestRoot, "responses/allowed.json"))
	require.NoError(t, err)

	allowedWithPayloadPath := path.Join(testhelper.TestRoot, "responses/allowed_with_push_payload.json")
	bodyWithPayload, err := ioutil.ReadFile(allowedWithPayloadPath)
	require.NoError(t, err)

	requests := []testserver.TestRequestHandler{
		{
			Path: "/api/v4/internal/allowed",
			Handler: func(w http.ResponseWriter, r *http.Request) {
				b, err := ioutil.ReadAll(r.Body)
				require.NoError(t, err)

				var requestBody *Request
				require.NoError(t, json.Unmarshal(b, &requestBody))

				switch requestBody.Username {
				case "first":
					_, err = w.Write(body)
					require.NoError(t, err)
				case "second":
					errBody := map[string]interface{}{
						"status":  false,
						"message": "missing user",
					}
					require.NoError(t, json.NewEncoder(w).Encode(errBody))
				case "custom":
					w.WriteHeader(http.StatusMultipleChoices)
					_, err = w.Write(bodyWithPayload)
					require.NoError(t, err)
				}

				switch requestBody.KeyId {
				case "1":
					_, err = w.Write(body)
					require.NoError(t, err)
				case "2":
					w.WriteHeader(http.StatusForbidden)
					errBody := &gitlabnet.ErrorResponse{
						Message: "Not allowed!",
					}
					require.NoError(t, json.NewEncoder(w).Encode(errBody))
				case "3":
					w.Write([]byte("{ \"message\": \"broken json!\""))
				case "4":
					w.WriteHeader(http.StatusForbidden)
				}
			},
		},
	}

	url, cleanup := testserver.StartSocketHttpServer(t, requests)

	client, err := NewClient(&config.Config{GitlabUrl: url})
	require.NoError(t, err)

	return client, cleanup
}
<|endoftext|>