<commit_before>package lang

import (
	"bufio"
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"github.com/dockpit/pit/contract"
)

var CaseEX = regexp.MustCompile(`^'(.*)'$`)
var ResourceEX = regexp.MustCompile(`^- (.*)`)
var VariableEX = regexp.MustCompile(`(\(.*?\))`)
var ValidHTTPMethods = []string{"GET", "POST", "PUT"} //@todo add more

func UnexpectedDirError(fi os.FileInfo) error {
	return fmt.Errorf("Parser encountered an unexpected directory: %s, expected a resource directory (starting with `- `), or a case directory formatted as `'case name'`", fi.Name())
}

func UnexpectedFileError(fi os.FileInfo) error {
	return fmt.Errorf("Parser encountered a file without an extension: '%s', only 'given', 'when', 'then' or 'while' is allowed", fi.Name())
}

func UnexpectedStateLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'given' file '%s', with an invalid line: \n%s\n expected format \"<state provider name> '<state name>'\"", fpath, line)
}

func UnexpectedLinkLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'while' file '%s' with an unexpected line: %s, expected format \"<service id> '<case name>'\"", fpath, line)
}

func UnexpectedLinkLineCaseNameError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'while' file '%s' with a invalid casename: \"%s\", expected single-quoted name: e.g 'name of the case'", fpath, line)
}

func UnexpectedHeaderLineError(fpath, giv string) error {
	return fmt.Errorf("File '%s' has an unexpected header line: '%s', expected format 'Header-Key: Value'", fpath, giv)
}

func UnexpectedResponseLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'then' file '%s' with an unexpected first line: %s, expected format '<HTTP Status Code> <Status Text>'", fpath, line)
}

func UnexpectedResponseLineCodeError(fpath, giv string, err error) error {
	return fmt.Errorf("Parser encountered a 'then' file '%s' with an unexpected status code: %s, expected a number. (%s)", fpath, giv, err)
}

func UnexpectedRequestLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'when' file '%s' with an unexpected first line: %s, expected format '<HTTP method> <path>'", fpath, line)
}

func UnexpectedRequestLineMethodError(fpath, giv string) error {
	return fmt.Errorf("Parser encountered a 'when' file '%s' with an unexpected HTTP Method in the first line: '%s', expected one of: %s", fpath, giv, ValidHTTPMethods)
}

func UnexpectedRequestLinePathError(fpath, giv string) error {
	return fmt.Errorf("Parser encountered a 'when' file '%s' with an unexpected path in the first line: '%s', expected absolute path (starting with '/')", fpath, giv)
}

type Node struct {
	Pattern  string
	Children []*Node
}

func NewNode() *Node {
	return &Node{
		Pattern:  "/",
		Children: []*Node{},
	}
}

func (n *Node) Append(nn *Node, part string) {
	n.Children = append(n.Children, nn)

	nn.Pattern = path.Join(n.Pattern, part)
}

//
//
//
type Parser struct {
	Dir string

	data  *contract.ContractData
	nodes map[string]*Node
	cases map[string]string

	currentNode     *Node
	currentResource *contract.ResourceData
	currentCase     *contract.CaseData
}

func NewParser(dir string) *Parser {
	p := &Parser{
		Dir: dir,
	}
	p.reset()
	return p
}

func (p *Parser) reset() {
	root := NewNode()
	p.data = &contract.ContractData{}
	p.nodes = map[string]*Node{".": root}
	p.cases = map[string]string{}
	p.currentNode = root
	p.currentResource = nil
	p.currentCase = nil
}

func (p *Parser) ParseHTTPMessage(r io.ReadCloser, fpath string) (string, http.Header, string, error) {

	inbody := false
	rline := ""
	hlines := []string{}
	body := ""
	headers := make(http.Header)

	s := bufio.NewScanner(r)
	for s.Scan() {
		if !inbody {
			//first line should be the request line
			if rline == "" {
				rline = s.Text()
				continue
			}

			//empty line indicates that we will be parsing the body
			if s.Text() == "" {
				inbody = true
				continue
			}

			//add as headers
			hlines = append(hlines, s.Text())
		} else {
			//res is assumed to be body
			body += s.Text()
		}
	}

	//check/parse header format
	for _, h := range hlines {
		hp := strings.SplitN(h, ":", 2)
		if len(hp) != 2 {
			return rline, headers, body, UnexpectedHeaderLineError(fpath, h)
		}

		headers.Add(http.CanonicalHeaderKey(hp[0]), strings.TrimSpace(hp[1]))
	}

	return rline, headers, body, nil
}

// parses a when file loosely based on the http request spec
func (p *Parser) ParseWhen(r io.ReadCloser, fpath string) (*contract.When, error) {
	w := &contract.When{}

	rline, headers, body, err := p.ParseHTTPMessage(r, fpath)
	if err != nil {
		return nil, err
	}

	//request line should be 2 parts seperated by a space
	rlinep := strings.SplitN(rline, " ", 2)
	if len(rlinep) != 2 {
		return nil, UnexpectedRequestLineError(fpath, rline)
	}

	//check method format
	method := ""
	for _, m := range ValidHTTPMethods {
		if m == rlinep[0] {
			method = m
			break
		}
	}

	if method == "" {
		return nil, UnexpectedRequestLineMethodError(fpath, rlinep[0])
	}
	w.Method = method

	//check path
	if !path.IsAbs(rlinep[1]) {
		return nil, UnexpectedRequestLinePathError(fpath, rlinep[1])
	}
	w.Path = rlinep[1]
	w.Headers = headers
	w.Body = body

	return w, nil
}

// parses a then file loosely based on the format of a standard http message
func (p *Parser) ParseThen(r io.ReadCloser, fpath string) (*contract.Then, error) {
	t := &contract.Then{}

	//parse as a standard http message
	rline, headers, body, err := p.ParseHTTPMessage(r, fpath)
	if err != nil {
		return nil, err
	}

	//response line should be 2 parts seperated by a space
	rlinep := strings.SplitN(rline, " ", 2)
	if len(rlinep) != 2 {
		return nil, UnexpectedResponseLineError(fpath, rline)
	}

	//first one should be parseable as int
	code, err := strconv.Atoi(rlinep[0])
	if err != nil {
		return nil, UnexpectedResponseLineCodeError(fpath, rlinep[0], err)
	}

	t.StatusCode = code
	t.Status = rlinep[1]
	t.Headers = headers
	t.Body = body
	return t, nil
}

// parses a 'while' file
func (p *Parser) ParseWhile(r io.ReadCloser, fpath string) ([]contract.While, error) {
	ws := []contract.While{}

	s := bufio.NewScanner(r)
	for s.Scan() {

		//dont mind empty lines
		if strings.TrimSpace(s.Text()) == "" {
			continue
		}

		//every non-empty line should have space seperated link
		wp := strings.SplitN(s.Text(), " ", 2)
		if len(wp) != 2 {
			return ws, UnexpectedLinkLineError(fpath, s.Text())
		}

		cname := p.ToCaseName(wp[1])
		if cname == "" {
			return ws, UnexpectedLinkLineCaseNameError(fpath, wp[1])
		}

		//create while for case
		ws = append(ws, contract.While{
			ID:       wp[0],
			CaseName: cname,
		})
	}

	return ws, nil
}

// parses a 'given' file
func (p *Parser) ParseGiven(r io.ReadCloser, fpath string) (map[string]contract.Given, error) {
	gs := make(map[string]contract.Given)

	s := bufio.NewScanner(r)
	for s.Scan() {

		//dont mind empty lines
		if strings.TrimSpace(s.Text()) == "" {
			continue
		}

		//every non-empty line should have space seperated link
		gp := strings.SplitN(s.Text(), ":", 2)
		if len(gp) != 2 {
			return gs, UnexpectedStateLineError(fpath, s.Text())
		}

		//extract provider name
		pname := strings.TrimSpace(gp[0])
		if pname == "" {
			return gs, UnexpectedStateLineError(fpath, s.Text())
		}

		//extract state name as case name
		sname := p.ToCaseName(strings.TrimSpace(gp[1]))
		if sname == "" {
			return gs, UnexpectedStateLineError(fpath, s.Text())
		}

		//set given
		gs[pname] = contract.Given{
			Name: sname,
		}
	}

	return gs, nil
}

//
// Returns wether a given basename of a file path denotes a resource
func (p *Parser) ToResourcePatternPart(basename string) string {
	m := ResourceEX.FindStringSubmatch(basename)
	if m == nil {
		return ""
	}

	//replace variables by sinatra style vars
	res := VariableEX.ReplaceAllFunc([]byte(m[1]), func(in []byte) []byte {
		v := in[1 : len(in)-1]
		return append([]byte(":"), v...)
	})

	return string(res)
}

//
// Returns wether a given basename of a file path denotes a case example
func (p *Parser) ToCaseName(basename string) string {
	m := CaseEX.FindStringSubmatch(basename)
	if m == nil {
		return ""
	}

	return m[1]
}

func (p *Parser) visit(fpath string, fi os.FileInfo, err error) error {

	//cancel walk if something went wrong
	if err != nil {
		return err
	}

	//skip root
	if fpath == p.Dir {
		return nil
	}

	//care only about relative path
	rel, err := filepath.Rel(p.Dir, fpath)
	if err != nil {
		return err
	}

	//directories are expected to be either resources or cases
	if fi.IsDir() {
		if part := p.ToResourcePatternPart(filepath.Base(rel)); part != "" {
			var parent *Node
			var ok bool

			//create and add new resource node
			p.currentNode = NewNode()
			p.nodes[rel] = p.currentNode

			//get parent node
			if parent, ok = p.nodes[filepath.Dir(rel)]; !ok {
				return fmt.Errorf("No parent node found for '%s'", fpath)
			}

			//apprent to parent
			parent.Append(p.currentNode, part)

			//use node structure to create and append resource to contract
			p.currentResource = &contract.ResourceData{
				Pattern: p.currentNode.Pattern,
				Cases:   []*contract.CaseData{},
			}

			// add to contract data
			p.data.Resources = append(p.data.Resources, p.currentResource)

		} else if cname := p.ToCaseName(filepath.Base(rel)); cname != "" {

			//get current resource (if any)
			res := p.currentResource
			if res == nil {
				return fmt.Errorf("Case folder '%s' (%s) is outside a resource", cname, fpath)
			}

			//case name must be unique
			if ex, ok := p.cases[cname]; ok {
				return fmt.Errorf("Case with name '%s' (%s) already exists in '%s'", cname, fpath, filepath.Dir(ex))
			}

			//create the case from available data
			p.currentCase = &contract.CaseData{
				Name: cname,
			}

			//and append to resource
			res.Cases = append(res.Cases, p.currentCase)
			p.cases[cname] = fpath
		} else {
			return UnexpectedDirError(fi)
		}
	} else {

		//files without extension have to be either when/then/given/while
		if filepath.Ext(fpath) == "" {

			f, err := os.Open(fpath)
			if err != nil {
				return err
			}
			defer f.Close()

			//'keywords;
			if filepath.Base(fpath) == "given" {
				given, err := p.ParseGiven(f, fpath)
				if err != nil {
					return err
				}

				p.currentCase.Given = given
			} else if filepath.Base(fpath) == "when" {
				when, err := p.ParseWhen(f, fpath)
				if err != nil {
					return err
				}

				p.currentCase.When = *when
			} else if filepath.Base(fpath) == "then" {
				then, err := p.ParseThen(f, fpath)
				if err != nil {
					return err
				}

				fmt.Println("AAAAAA", p.currentCase, then)

				p.currentCase.Then = *then
			} else if filepath.Base(fpath) == "while" {
				whiles, err := p.ParseWhile(f, fpath)
				if err != nil {
					return err
				}

				p.currentCase.While = whiles
			} else {
				return UnexpectedFileError(fi)
			}

		} else {
			//@todo parse data files
		}

	}

	return nil
}

func (p *Parser) Parse() (*contract.ContractData, error) {

	//reset parser afterwards
	defer p.reset()

	//walk nodes
	err := filepath.Walk(p.Dir, p.visit)
	if err != nil {
		return nil, err
	}

	//return result
	return p.data, nil
}
<commit_msg>debugging...<commit_after>package lang

import (
	"bufio"
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"github.com/dockpit/pit/contract"
)

var CaseEX = regexp.MustCompile(`^'(.*)'$`)
var ResourceEX = regexp.MustCompile(`^- (.*)`)
var VariableEX = regexp.MustCompile(`(\(.*?\))`)
var ValidHTTPMethods = []string{"GET", "POST", "PUT"} //@todo add more

func UnexpectedDirError(fi os.FileInfo) error {
	return fmt.Errorf("Parser encountered an unexpected directory: %s, expected a resource directory (starting with `- `), or a case directory formatted as `'case name'`", fi.Name())
}

func UnexpectedFileError(fi os.FileInfo) error {
	return fmt.Errorf("Parser encountered a file without an extension: '%s', only 'given', 'when', 'then' or 'while' is allowed", fi.Name())
}

func UnexpectedStateLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'given' file '%s', with an invalid line: \n%s\n expected format \"<state provider name> '<state name>'\"", fpath, line)
}

func UnexpectedLinkLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'while' file '%s' with an unexpected line: %s, expected format \"<service id> '<case name>'\"", fpath, line)
}

func UnexpectedLinkLineCaseNameError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'while' file '%s' with a invalid casename: \"%s\", expected single-quoted name: e.g 'name of the case'", fpath, line)
}

func UnexpectedHeaderLineError(fpath, giv string) error {
	return fmt.Errorf("File '%s' has an unexpected header line: '%s', expected format 'Header-Key: Value'", fpath, giv)
}

func UnexpectedResponseLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'then' file '%s' with an unexpected first line: %s, expected format '<HTTP Status Code> <Status Text>'", fpath, line)
}

func UnexpectedResponseLineCodeError(fpath, giv string, err error) error {
	return fmt.Errorf("Parser encountered a 'then' file '%s' with an unexpected status code: %s, expected a number. (%s)", fpath, giv, err)
}

func UnexpectedRequestLineError(fpath, line string) error {
	return fmt.Errorf("Parser encountered a 'when' file '%s' with an unexpected first line: %s, expected format '<HTTP method> <path>'", fpath, line)
}

func UnexpectedRequestLineMethodError(fpath, giv string) error {
	return fmt.Errorf("Parser encountered a 'when' file '%s' with an unexpected HTTP Method in the first line: '%s', expected one of: %s", fpath, giv, ValidHTTPMethods)
}

func UnexpectedRequestLinePathError(fpath, giv string) error {
	return fmt.Errorf("Parser encountered a 'when' file '%s' with an unexpected path in the first line: '%s', expected absolute path (starting with '/')", fpath, giv)
}

type Node struct {
	Pattern  string
	Children []*Node
}

func NewNode() *Node {
	return &Node{
		Pattern:  "/",
		Children: []*Node{},
	}
}

func (n *Node) Append(nn *Node, part string) {
	n.Children = append(n.Children, nn)

	nn.Pattern = path.Join(n.Pattern, part)
}

//
//
//
type Parser struct {
	Dir string

	data  *contract.ContractData
	nodes map[string]*Node
	cases map[string]string

	currentNode     *Node
	currentResource *contract.ResourceData
	currentCase     *contract.CaseData
}

func NewParser(dir string) *Parser {
	p := &Parser{
		Dir: dir,
	}
	p.reset()
	return p
}

func (p *Parser) reset() {
	root := NewNode()
	p.data = &contract.ContractData{}
	p.nodes = map[string]*Node{".": root}
	p.cases = map[string]string{}
	p.currentNode = root
	p.currentResource = nil
	p.currentCase = nil
}

func (p *Parser) ParseHTTPMessage(r io.ReadCloser, fpath string) (string, http.Header, string, error) {

	inbody := false
	rline := ""
	hlines := []string{}
	body := ""
	headers := make(http.Header)

	s := bufio.NewScanner(r)
	for s.Scan() {
		if !inbody {
			//first line should be the request line
			if rline == "" {
				rline = s.Text()
				continue
			}

			//empty line indicates that we will be parsing the body
			if s.Text() == "" {
				inbody = true
				continue
			}

			//add as headers
			hlines = append(hlines, s.Text())
		} else {
			//res is assumed to be body
			body += s.Text()
		}
	}

	//check/parse header format
	for _, h := range hlines {
		hp := strings.SplitN(h, ":", 2)
		if len(hp) != 2 {
			return rline, headers, body, UnexpectedHeaderLineError(fpath, h)
		}

		headers.Add(http.CanonicalHeaderKey(hp[0]), strings.TrimSpace(hp[1]))
	}

	return rline, headers, body, nil
}

// parses a when file loosely based on the http request spec
func (p *Parser) ParseWhen(r io.ReadCloser, fpath string) (*contract.When, error) {
	w := &contract.When{}

	rline, headers, body, err := p.ParseHTTPMessage(r, fpath)
	if err != nil {
		return nil, err
	}

	//request line should be 2 parts seperated by a space
	rlinep := strings.SplitN(rline, " ", 2)
	if len(rlinep) != 2 {
		return nil, UnexpectedRequestLineError(fpath, rline)
	}

	//check method format
	method := ""
	for _, m := range ValidHTTPMethods {
		if m == rlinep[0] {
			method = m
			break
		}
	}

	if method == "" {
		return nil, UnexpectedRequestLineMethodError(fpath, rlinep[0])
	}
	w.Method = method

	//check path
	if !path.IsAbs(rlinep[1]) {
		return nil, UnexpectedRequestLinePathError(fpath, rlinep[1])
	}
	w.Path = rlinep[1]
	w.Headers = headers
	w.Body = body

	return w, nil
}

// parses a then file loosely based on the format of a standard http message
func (p *Parser) ParseThen(r io.ReadCloser, fpath string) (*contract.Then, error) {
	t := &contract.Then{}

	//parse as a standard http message
	rline, headers, body, err := p.ParseHTTPMessage(r, fpath)
	if err != nil {
		return nil, err
	}

	//response line should be 2 parts seperated by a space
	rlinep := strings.SplitN(rline, " ", 2)
	if len(rlinep) != 2 {
		return nil, UnexpectedResponseLineError(fpath, rline)
	}

	//first one should be parseable as int
	code, err := strconv.Atoi(rlinep[0])
	if err != nil {
		return nil, UnexpectedResponseLineCodeError(fpath, rlinep[0], err)
	}

	t.StatusCode = code
	t.Status = rlinep[1]
	t.Headers = headers
	t.Body = body
	return t, nil
}

// parses a 'while' file
func (p *Parser) ParseWhile(r io.ReadCloser, fpath string) ([]contract.While, error) {
	ws := []contract.While{}

	s := bufio.NewScanner(r)
	for s.Scan() {

		//dont mind empty lines
		if strings.TrimSpace(s.Text()) == "" {
			continue
		}

		//every non-empty line should have space seperated link
		wp := strings.SplitN(s.Text(), " ", 2)
		if len(wp) != 2 {
			return ws, UnexpectedLinkLineError(fpath, s.Text())
		}

		cname := p.ToCaseName(wp[1])
		if cname == "" {
			return ws, UnexpectedLinkLineCaseNameError(fpath, wp[1])
		}

		//create while for case
		ws = append(ws, contract.While{
			ID:       wp[0],
			CaseName: cname,
		})
	}

	return ws, nil
}

// parses a 'given' file
func (p *Parser) ParseGiven(r io.ReadCloser, fpath string) (map[string]contract.Given, error) {
	gs := make(map[string]contract.Given)

	s := bufio.NewScanner(r)
	for s.Scan() {

		//dont mind empty lines
		if strings.TrimSpace(s.Text()) == "" {
			continue
		}

		//every non-empty line should have space seperated link
		gp := strings.SplitN(s.Text(), ":", 2)
		if len(gp) != 2 {
			return gs, UnexpectedStateLineError(fpath, s.Text())
		}

		//extract provider name
		pname := strings.TrimSpace(gp[0])
		if pname == "" {
			return gs, UnexpectedStateLineError(fpath, s.Text())
		}

		//extract state name as case name
		sname := p.ToCaseName(strings.TrimSpace(gp[1]))
		if sname == "" {
			return gs, UnexpectedStateLineError(fpath, s.Text())
		}

		//set given
		gs[pname] = contract.Given{
			Name: sname,
		}
	}

	return gs, nil
}

//
// Returns wether a given basename of a file path denotes a resource
func (p *Parser) ToResourcePatternPart(basename string) string {
	m := ResourceEX.FindStringSubmatch(basename)
	if m == nil {
		return ""
	}

	//replace variables by sinatra style vars
	res := VariableEX.ReplaceAllFunc([]byte(m[1]), func(in []byte) []byte {
		v := in[1 : len(in)-1]
		return append([]byte(":"), v...)
	})

	return string(res)
}

//
// Returns wether a given basename of a file path denotes a case example
func (p *Parser) ToCaseName(basename string) string {
	m := CaseEX.FindStringSubmatch(basename)
	if m == nil {
		return ""
	}

	return m[1]
}

func (p *Parser) visit(fpath string, fi os.FileInfo, err error) error {

	//cancel walk if something went wrong
	if err != nil {
		return err
	}

	//skip root
	if fpath == p.Dir {
		return nil
	}

	//care only about relative path
	rel, err := filepath.Rel(p.Dir, fpath)
	if err != nil {
		return err
	}

	//directories are expected to be either resources or cases
	if fi.IsDir() {
		if part := p.ToResourcePatternPart(filepath.Base(rel)); part != "" {
			var parent *Node
			var ok bool

			//create and add new resource node
			p.currentNode = NewNode()
			p.nodes[rel] = p.currentNode

			//get parent node
			if parent, ok = p.nodes[filepath.Dir(rel)]; !ok {
				return fmt.Errorf("No parent node found for '%s'", fpath)
			}

			//apprent to parent
			parent.Append(p.currentNode, part)

			//use node structure to create and append resource to contract
			p.currentResource = &contract.ResourceData{
				Pattern: p.currentNode.Pattern,
				Cases:   []*contract.CaseData{},
			}

			// add to contract data
			p.data.Resources = append(p.data.Resources, p.currentResource)

		} else if cname := p.ToCaseName(filepath.Base(rel)); cname != "" {

			//get current resource (if any)
			res := p.currentResource
			if res == nil {
				return fmt.Errorf("Case folder '%s' (%s) is outside a resource", cname, fpath)
			}

			//case name must be unique
			if ex, ok := p.cases[cname]; ok {
				return fmt.Errorf("Case with name '%s' (%s) already exists in '%s'", cname, fpath, filepath.Dir(ex))
			}

			//create the case from available data
			p.currentCase = &contract.CaseData{
				Name: cname,
			}

			//and append to resource
			res.Cases = append(res.Cases, p.currentCase)
			p.cases[cname] = fpath
		} else {
			return UnexpectedDirError(fi)
		}
	} else {

		//files without extension have to be either when/then/given/while
		if filepath.Ext(fpath) == "" {

			f, err := os.Open(fpath)
			if err != nil {
				return err
			}
			defer f.Close()

			//'keywords;
			if filepath.Base(fpath) == "given" {
				given, err := p.ParseGiven(f, fpath)
				if err != nil {
					return err
				}

				p.currentCase.Given = given
			} else if filepath.Base(fpath) == "when" {
				when, err := p.ParseWhen(f, fpath)
				if err != nil {
					return err
				}

				p.currentCase.When = *when
			} else if filepath.Base(fpath) == "then" {
				then, err := p.ParseThen(f, fpath)
				if err != nil {
					return err
				}

				fmt.Println("AAAAAA", p.currentCase, p.currentCase.Then, then)
				p.currentCase.Then = *then
			} else if filepath.Base(fpath) == "while" {
				whiles, err := p.ParseWhile(f, fpath)
				if err != nil {
					return err
				}

				p.currentCase.While = whiles
			} else {
				return UnexpectedFileError(fi)
			}

		} else {
			//@todo parse data files
		}

	}

	return nil
}

func (p *Parser) Parse() (*contract.ContractData, error) {

	//reset parser afterwards
	defer p.reset()

	//walk nodes
	err := filepath.Walk(p.Dir, p.visit)
	if err != nil {
		return nil, err
	}

	//return result
	return p.data, nil
}
<|endoftext|>


<commit_before>/*
 * boardgame-mysql-admin helps create and migrate sql databases for boardgame.
 */
package main

import (
	"flag"
	"fmt"
	"github.com/jkomoros/boardgame/server/config"
	"github.com/jkomoros/boardgame/storage/mysql/connect"
	"github.com/mattes/migrate"
	"log"
	"os"
)

type appOptions struct {
	Help    bool
	Prod    bool
	flagSet *flag.FlagSet
}

func defineFlags(options *appOptions) {
	options.flagSet.BoolVar(&options.Help, "help", false, "If true, will print help and exit.")
	options.flagSet.BoolVar(&options.Prod, "prod", false, "If true will operate on prod. If omitted will default to dev")
}

func getOptions(flagSet *flag.FlagSet, flagArguments []string) *appOptions {
	options := &appOptions{flagSet: flagSet}
	defineFlags(options)
	flagSet.Parse(flagArguments)
	return options
}

func main() {
	flagSet := flag.CommandLine
	process(getOptions(flagSet, os.Args[1:]))
}

func process(options *appOptions) {

	if options.Help {
		doHelp()
		return
	}

	cfg, err := config.Get()

	if err != nil {
		log.Println("invalid config: " + err.Error())
		return
	}

	configToUse := cfg.Dev

	if options.Prod {
		configToUse = cfg.Prod
	}

	if configToUse.StorageConfig["mysql"] == "" {
		log.Println("No connection string configured for mysql")
		return
	}

	db, err := connect.Db(configToUse.StorageConfig["mysql"], false, false)

	if err != nil {
		log.Println(err)
		return
	}

	m, err := connect.Migrations(db)

	if err != nil {
		log.Println("Couldnt' create migration instance: " + err.Error())
		return
	}

	switch options.flagSet.Arg(0) {
	case "up":
		if prodConfirm(options.Prod) {
			doUp(m)
		}

	case "down":
		if prodConfirm(options.Prod) {
			doDown(m)
		}
	default:
		doVersion(m)
	}

}

func prodConfirm(isProd bool) bool {
	if !isProd {
		return true
	}
	log.Println("You have selected a destructive action on prod. Are you sure? (y/N)")
	var response string
	fmt.Scanln(&response)
	yesResponses := []string{"Yes", "Y", "yes"}
	for _, responseToTest := range yesResponses {
		if response == responseToTest {
			return true
		}
	}
	return false
}

func doHelp() {
	help := `Commands: 'version', 'up', 'down'`
	log.Println(help)
}

func doUp(m *migrate.Migrate) {
	if err := m.Up(); err != nil {
		if err == migrate.ErrNoChange {
			log.Println("Already up to date")
		} else {
			log.Println("Up failed: " + err.Error())
		}
	}
}

func doDown(m *migrate.Migrate) {
	if err := m.Down(); err != nil {
		if err == migrate.ErrNoChange {
			log.Println("Already at version 0")
		} else {
			log.Println("Down failed: " + err.Error())
		}
	}
}

func doVersion(m *migrate.Migrate) {
	version, _, _ := m.Version()
	log.Println("Version: ", version)
}
<commit_msg>Added 'setup' command to boardgame-mysql-admin that will create the database if it doesn't exist. Part of #273.<commit_after>/*
 * boardgame-mysql-admin helps create and migrate sql databases for boardgame.
 */
package main

import (
	"flag"
	"fmt"
	"github.com/jkomoros/boardgame/server/config"
	"github.com/jkomoros/boardgame/storage/mysql/connect"
	"github.com/mattes/migrate"
	"log"
	"os"
	"strings"
)

type appOptions struct {
	Help    bool
	Prod    bool
	flagSet *flag.FlagSet
}

func defineFlags(options *appOptions) {
	options.flagSet.BoolVar(&options.Help, "help", false, "If true, will print help and exit.")
	options.flagSet.BoolVar(&options.Prod, "prod", false, "If true will operate on prod. If omitted will default to dev")
}

func getOptions(flagSet *flag.FlagSet, flagArguments []string) *appOptions {
	options := &appOptions{flagSet: flagSet}
	defineFlags(options)
	flagSet.Parse(flagArguments)
	return options
}

func main() {
	flagSet := flag.CommandLine
	process(getOptions(flagSet, os.Args[1:]))
}

func process(options *appOptions) {

	if options.Help {
		doHelp()
		return
	}

	cfg, err := config.Get()

	if err != nil {
		log.Println("invalid config: " + err.Error())
		return
	}

	configToUse := cfg.Dev

	if options.Prod {
		configToUse = cfg.Prod
	}

	if configToUse.StorageConfig["mysql"] == "" {
		log.Println("No connection string configured for mysql")
		return
	}

	createDb := false
	if strings.ToLower(options.flagSet.Arg(0)) == "setup" {
		createDb = true
	}

	db, err := connect.Db(configToUse.StorageConfig["mysql"], false, createDb)

	if err != nil {
		log.Println(err)
		return
	}

	m, err := connect.Migrations(db)

	if err != nil {
		log.Println("Couldnt' create migration instance: " + err.Error())
		return
	}

	switch strings.ToLower(options.flagSet.Arg(0)) {
	case "up", "setup":
		if prodConfirm(options.Prod) {
			doUp(m)
		}

	case "down":
		if prodConfirm(options.Prod) {
			doDown(m)
		}
	default:
		doVersion(m)
	}

}

func prodConfirm(isProd bool) bool {
	if !isProd {
		return true
	}
	log.Println("You have selected a destructive action on prod. Are you sure? (y/N)")
	var response string
	fmt.Scanln(&response)
	yesResponses := []string{"Yes", "Y", "yes"}
	for _, responseToTest := range yesResponses {
		if response == responseToTest {
			return true
		}
	}
	return false
}

func doHelp() {
	help := `Commands:
* 'version' = Print version of migration and quit.
* 'up' = Apply all migrations forward (run on an existing db to make sure it's up to date)
* 'down' = Apply all migrations downward
* 'setup' = Create the 'boardgame' database and apply all migrations forward`
	log.Println(help)
}

func doUp(m *migrate.Migrate) {
	if err := m.Up(); err != nil {
		if err == migrate.ErrNoChange {
			log.Println("Already up to date")
		} else {
			log.Println("Up failed: " + err.Error())
		}
	}
}

func doDown(m *migrate.Migrate) {
	if err := m.Down(); err != nil {
		if err == migrate.ErrNoChange {
			log.Println("Already at version 0")
		} else {
			log.Println("Down failed: " + err.Error())
		}
	}
}

func doVersion(m *migrate.Migrate) {
	version, _, _ := m.Version()
	log.Println("Version: ", version)
}
<|endoftext|>


<commit_before>/*
Package template documents the way user-defined output templates are
ment to be used.

User-defined templates use Go's text/template package, so you might
want to check its documentation first.
There are a bunch of helper methods available inside a template
besides those described in aforementioned documentation, namely:
	- WithLatencies()
		Tells whether --latencies flag were activated.
	- FormatBinary(numberOfBytes float64) string
		Converts bytes to kilo-, mega-, giga-, etc.- bytes, and
		appends appropriate suffix "KB", "MB", "GB", etc.
	- FormatTimeUs(us float64) string
		Converts microseconds to milliseconds, seconds, minutes or
		hours and appends appropriate suffix.
	- FormatTimeUsUint64(us uint64) string
		Same as above, but for uint64, since type conversions are
		not available in templates.
	- FloatsToArray(ps ...float64) []float64
		Converts a bunch of floats into array, since, again,
		type conversions are not available in templates.
	- Multiply(num, coeff float64) float64
		Arithmetics are not available inside of templates either.
	- StringToBytes(s string) []byte
		Convenience function to convert string to []byte.
	- UUIDV1() (UUID, error)
		Generates UUID Version 1, based on timestamp and
		MAC address (RFC 4122)
	- UUIDV2(domain byte) (UUID, error)
		Generates UUID Version 2, based on timestamp, MAC address
		and POSIX UID/GID (DCE 1.1)
	- UUIDV3(ns UUID, name string) UUID
		Generates UUID Version 3, based on MD5 hashing (RFC 4122)
	- UUIDV4() (UUID, error)
		Generates UUID Version 4, based on random numbers (RFC 4122)
	- UUIDV5(ns UUID, name string) UUID
		Generates UUID Version 5, based on SHA-1 hashing (RFC 4122)

The structure that gets passed to the template is documented in
the package github.com/codesenberg/bombardier/internal. The structure
of interest is TestInfo. It basically consists of Spec and Result
fields, the former contains various information about the test
(number of connections, URL, HTTP method, headers, body, rate, etc.)
performed, while the latter contains results obtained during the
execution of this test (bytes read/written, time taken, RPS, etc.).

Link to GoDoc for the structure used in template:
https://godoc.org/github.com/codesenberg/bombardier/internal#TestInfo

Examples of templates can be found in:
https://github.com/codesenberg/bombardier/blob/master/template.go
*/
package template
<commit_msg>doc: fix link to examples<commit_after>/*
Package template documents the way user-defined output templates are
ment to be used.

User-defined templates use Go's text/template package, so you might
want to check its documentation first.
There are a bunch of helper methods available inside a template
besides those described in aforementioned documentation, namely:
	- WithLatencies()
		Tells whether --latencies flag were activated.
	- FormatBinary(numberOfBytes float64) string
		Converts bytes to kilo-, mega-, giga-, etc.- bytes, and
		appends appropriate suffix "KB", "MB", "GB", etc.
	- FormatTimeUs(us float64) string
		Converts microseconds to milliseconds, seconds, minutes or
		hours and appends appropriate suffix.
	- FormatTimeUsUint64(us uint64) string
		Same as above, but for uint64, since type conversions are
		not available in templates.
	- FloatsToArray(ps ...float64) []float64
		Converts a bunch of floats into array, since, again,
		type conversions are not available in templates.
	- Multiply(num, coeff float64) float64
		Arithmetics are not available inside of templates either.
	- StringToBytes(s string) []byte
		Convenience function to convert string to []byte.
	- UUIDV1() (UUID, error)
		Generates UUID Version 1, based on timestamp and
		MAC address (RFC 4122)
	- UUIDV2(domain byte) (UUID, error)
		Generates UUID Version 2, based on timestamp, MAC address
		and POSIX UID/GID (DCE 1.1)
	- UUIDV3(ns UUID, name string) UUID
		Generates UUID Version 3, based on MD5 hashing (RFC 4122)
	- UUIDV4() (UUID, error)
		Generates UUID Version 4, based on random numbers (RFC 4122)
	- UUIDV5(ns UUID, name string) UUID
		Generates UUID Version 5, based on SHA-1 hashing (RFC 4122)

The structure that gets passed to the template is documented in
the package github.com/codesenberg/bombardier/internal. The structure
of interest is TestInfo. It basically consists of Spec and Result
fields, the former contains various information about the test
(number of connections, URL, HTTP method, headers, body, rate, etc.)
performed, while the latter contains results obtained during the
execution of this test (bytes read/written, time taken, RPS, etc.).

Link to GoDoc for the structure used in template:
https://godoc.org/github.com/codesenberg/bombardier/internal#TestInfo

Examples of templates can be found in:
https://github.com/codesenberg/bombardier/blob/master/templates.go
*/
package template
<|endoftext|>


<commit_before>package server

import (
	"fmt"

	"github.com/pachyderm/pachyderm/src/client"

	"github.com/pachyderm/pachyderm/src/client/deploy"
	"golang.org/x/net/context"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kube "k8s.io/client-go/kubernetes"
)

type apiServer struct {
	kubeClient    *kube.Clientset
	kubeNamespace string
}

// NewDeployServer creates a deploy server
func NewDeployServer(kubeClient *kube.Clientset, kubeNamespace string) deploy.APIServer {
	return &apiServer{
		kubeClient:    kubeClient,
		kubeNamespace: kubeNamespace,
	}
}

func (s *apiServer) DeployStorageSecret(ctx context.Context, req *deploy.DeployStorageSecretRequest) (*deploy.DeployStorageSecretResponse, error) {
	kubeSecrets := s.kubeClient.CoreV1().Secrets(s.kubeNamespace)

	secret, err := kubeSecrets.Get(client.StorageSecretName, metav1.GetOptions{})
	if err != nil {
		return nil, fmt.Errorf("error retrieving secret from kubernetes: %v", err)
	}

	// merge the secrets
	if secret.Data == nil {
		secret.Data = make(map[string][]byte)
	}
	for key, val := range req.Secrets {
		secret.Data[key] = val
	}

	if _, err := kubeSecrets.Update(secret); err != nil {
		return nil, fmt.Errorf("error updating secret: %v", err)
	}

	return &deploy.DeployStorageSecretResponse{}, nil
}
<commit_msg>remove deploy server code<commit_after><|endoftext|>


<commit_before>package s3

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"fmt"
	"io"
	"net/http"
	"strings"
	"text/template"

	"github.com/gogo/protobuf/types"
	"github.com/gorilla/mux"
	log "github.com/sirupsen/logrus"

	"github.com/pachyderm/pachyderm/src/client"
)

const locationResponse = `<?xml version="1.0" encoding="UTF-8"?>
<LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/">PACHYDERM</LocationConstraint>`

const listBucketSource = `<?xml version="1.0" encoding="UTF-8"?>
<ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01">
    <Owner>
    	<ID>000000000000000000000000000000</ID>
    	<DisplayName>pachyderm</DisplayName>
    </Owner>
    <Buckets>
        {{ range . }}
            <Bucket>
                <Name>{{ .Repo.Name }}</Name>
                <CreationDate>{{ formatTime .Created }}</CreationDate>
            </Bucket>
        {{ end }}
    </Buckets>
</ListAllMyBucketsResult>`

func writeBadRequest(w http.ResponseWriter, err error) {
	http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
}

func writeMaybeNotFound(w http.ResponseWriter, err error) {
	if strings.Contains(err.Error(), "not found") {
		// This error message matches what the mux router returns when it 404s
		http.Error(w, "404 page not found", http.StatusNotFound)
	} else {
		writeServerError(w, err)
	}
}

func writeServerError(w http.ResponseWriter, err error) {
	http.Error(w, fmt.Sprintf("%v", err), http.StatusInternalServerError)
}

type handler struct {
	pc                  *client.APIClient
	listBucketsTemplate *template.Template
}

func newHandler(pc *client.APIClient) handler {
	funcMap := template.FuncMap{
		"formatTime": func(timestamp *types.Timestamp) string {
			return timestamp.String()
		},
	}

	listBucketsTemplate := template.Must(template.New("list-buckets").
		Funcs(funcMap).
		Parse(listBucketSource))

	return handler{
		pc:                  pc,
		listBucketsTemplate: listBucketsTemplate,
	}
}

func (h handler) ping(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte{})
}

func (h handler) root(w http.ResponseWriter, r *http.Request) {
	buckets, err := h.pc.ListRepo()
	if err != nil {
		writeServerError(w, err)
		return
	}

	if err = h.listBucketsTemplate.Execute(w, buckets); err != nil {
		writeServerError(w, err)
		return
	}

	w.Header().Set("Content-Type", "application/xml")
}

func (h handler) repo(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	repo := vars["repo"]

	if err := r.ParseForm(); err != nil {
		writeBadRequest(w, err)
		return
	}

	if _, err := h.pc.InspectRepo(repo); err != nil {
		writeMaybeNotFound(w, err)
		return
	}

	if _, ok := r.Form["location"]; ok {
		w.Header().Set("Content-Type", "application/xml")
		w.Write([]byte(locationResponse))
	} else {
		w.Write([]byte{})
	}
}

func (h handler) getObject(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	repo := vars["repo"]
	branch := vars["branch"]
	file := vars["file"]

	fileInfo, err := h.pc.InspectFile(repo, branch, file)
	if err != nil {
		writeMaybeNotFound(w, err)
		return
	}

	timestamp, err := types.TimestampFromProto(fileInfo.Committed)
	if err != nil {
		writeServerError(w, err)
		return
	}

	reader, err := h.pc.GetFileReadSeeker(repo, branch, file)
	if err != nil {
		writeServerError(w, err)
		return
	}

	http.ServeContent(w, r, "", timestamp, reader)
}

func (h handler) putObject(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	repo := vars["repo"]
	branch := vars["branch"]
	file := vars["file"]

	expectedHash := r.Header.Get("Content-MD5")

	if expectedHash != "" {
		expectedHashBytes, err := base64.StdEncoding.DecodeString(expectedHash)

		if err != nil {
			writeBadRequest(w, fmt.Errorf("could not decode `Content-MD5`, as it is not base64-encoded"))
			return
		}

		h.putObjectVerifying(w, r, repo, branch, file, expectedHashBytes)
		return
	}

	h.putObjectUnverified(w, r, repo, branch, file)
}

func (h handler) putObjectVerifying(w http.ResponseWriter, r *http.Request, repo, branch, file string, expectedHash []byte) {
	hasher := md5.New()
	reader := io.TeeReader(r.Body, hasher)

	_, err := h.pc.PutFileOverwrite(repo, branch, file, reader, 0)
	if err != nil {
		// the error may be because the repo or branch does not exist -
		// double-check that by inspecting the branch, so we can serve a 404
		// instead
		_, inspectError := h.pc.InspectBranch(repo, branch)
		writeMaybeNotFound(w, inspectError)
		return
	}

	actualHash := hasher.Sum(nil)

	if !bytes.Equal(expectedHash, actualHash) {
		err = fmt.Errorf("content checksums differ; expected=%x, actual=%x", expectedHash, actualHash)
		writeServerError(w, err)
		return
	}

	w.Write([]byte{})
}

func (h handler) putObjectUnverified(w http.ResponseWriter, r *http.Request, repo, branch, file string) {
	_, err := h.pc.PutFileOverwrite(repo, branch, file, r.Body, 0)
	if err != nil {
		// the error may be because the repo or branch does not exist -
		// double-check that by inspecting the branch, so we can serve a 404
		// instead
		_, inspectError := h.pc.InspectBranch(repo, branch)
		writeMaybeNotFound(w, inspectError)
		return
	}

	w.Write([]byte{})
}

// Server runs an HTTP server with an S3-like API for PFS. This allows you to
// use s3 clients to acccess PFS contents.
//
// This returns an `http.Server` instance. It is the responsibility of the
// caller to start the server. This also makes it possible for the caller to
// enable graceful shutdown if desired; see the `http` package for details.
//
// Bucket names correspond to repo names, and files are accessible via the s3
// key pattern "<branch>/<filepath>". For example, to get the file "a/b/c.txt"
// on the "foo" repo's "master" branch, you'd making an s3 get request with
// bucket = "foo", key = "master/a/b/c.txt".
//
// Note: in s3, bucket names are constrained by IETF RFC 1123, (and its
// predecessor RFC 952) but pachyderm's repo naming constraints are slightly
// more liberal. If the s3 client does any kind of bucket name validation
// (this includes minio), repos whose names do not comply with RFC 1123 will
// not be accessible.
//
// Note: In `s3cmd`, you must set the access key and secret key, even though
// this API will ignore them - otherwise, you'll get an opaque config error:
// https://github.com/s3tools/s3cmd/issues/845#issuecomment-464885959
func Server(pc *client.APIClient, port uint16) *http.Server {
	handler := newHandler(pc)

	router := mux.NewRouter()
	router.HandleFunc("/", handler.root).Methods("GET")
	router.HandleFunc("/{repo}/", handler.repo).Methods("GET")
	router.HandleFunc("/{repo}/{branch}/{file:.+}", handler.getObject).Methods("GET")
	router.HandleFunc("/{repo}/{branch}/{file:.+}", handler.putObject).Methods("PUT")
	router.HandleFunc("/_ping", handler.ping).Methods("GET")

	// Note: error log is not customized on this `http.Server`, which means
	// it'll default to using the stdlib logger and produce log messages that
	// don't look like the ones produced elsewhere, and aren't configured
	// properly. In testing, this didn't seem to be a big deal because it's
	// rather hard to trigger it anyways, but if we find a reliable way to
	// create error logs, it might be worthwhile to fix.
	return &http.Server{
		Addr: fmt.Sprintf(":%d", port),
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			log.Debugf("s3 gateway request: %s %s", r.Method, r.RequestURI)
			router.ServeHTTP(w, r)
		}),
	}
}
<commit_msg>Implemented MakeBucket<commit_after>package s3

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"fmt"
	"io"
	"net/http"
	"strings"
	"text/template"

	"github.com/gogo/protobuf/types"
	"github.com/gorilla/mux"
	log "github.com/sirupsen/logrus"

	"github.com/pachyderm/pachyderm/src/client"
)

const locationResponse = `<?xml version="1.0" encoding="UTF-8"?>
<LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/">PACHYDERM</LocationConstraint>`

const listBucketSource = `<?xml version="1.0" encoding="UTF-8"?>
<ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01">
    <Owner>
    	<ID>000000000000000000000000000000</ID>
    	<DisplayName>pachyderm</DisplayName>
    </Owner>
    <Buckets>
        {{ range . }}
            <Bucket>
                <Name>{{ .Repo.Name }}</Name>
                <CreationDate>{{ formatTime .Created }}</CreationDate>
            </Bucket>
        {{ end }}
    </Buckets>
</ListAllMyBucketsResult>`

func writeBadRequest(w http.ResponseWriter, err error) {
	http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
}

func writeMaybeNotFound(w http.ResponseWriter, err error) {
	if strings.Contains(err.Error(), "not found") {
		// This error message matches what the mux router returns when it 404s
		http.Error(w, "404 page not found", http.StatusNotFound)
	} else {
		writeServerError(w, err)
	}
}

func writeServerError(w http.ResponseWriter, err error) {
	http.Error(w, fmt.Sprintf("%v", err), http.StatusInternalServerError)
}

type handler struct {
	pc                  *client.APIClient
	listBucketsTemplate *template.Template
}

func newHandler(pc *client.APIClient) handler {
	funcMap := template.FuncMap{
		"formatTime": func(timestamp *types.Timestamp) string {
			return timestamp.String()
		},
	}

	listBucketsTemplate := template.Must(template.New("list-buckets").
		Funcs(funcMap).
		Parse(listBucketSource))

	return handler{
		pc:                  pc,
		listBucketsTemplate: listBucketsTemplate,
	}
}

func (h handler) ping(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte{})
}

func (h handler) root(w http.ResponseWriter, r *http.Request) {
	buckets, err := h.pc.ListRepo()
	if err != nil {
		writeServerError(w, err)
		return
	}

	if err = h.listBucketsTemplate.Execute(w, buckets); err != nil {
		writeServerError(w, err)
		return
	}

	w.Header().Set("Content-Type", "application/xml")
}

func (h handler) repo(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	repo := vars["repo"]

	if r.Method == http.MethodGet {
		if err := r.ParseForm(); err != nil {
			writeBadRequest(w, err)
			return
		}

		if _, err := h.pc.InspectRepo(repo); err != nil {
			writeMaybeNotFound(w, err)
			return
		}

		if _, ok := r.Form["location"]; ok {
			w.Header().Set("Content-Type", "application/xml")
			w.Write([]byte(locationResponse))
		} else {
			w.Write([]byte{})
		}
	} else if r.Method == http.MethodPut {
		err := h.pc.CreateRepo(repo)

		if err != nil {
			writeServerError(w, err)
		} else {
			w.Write([]byte{})
		}
	}
}

func (h handler) getObject(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	repo := vars["repo"]
	branch := vars["branch"]
	file := vars["file"]

	fileInfo, err := h.pc.InspectFile(repo, branch, file)
	if err != nil {
		writeMaybeNotFound(w, err)
		return
	}

	timestamp, err := types.TimestampFromProto(fileInfo.Committed)
	if err != nil {
		writeServerError(w, err)
		return
	}

	reader, err := h.pc.GetFileReadSeeker(repo, branch, file)
	if err != nil {
		writeServerError(w, err)
		return
	}

	http.ServeContent(w, r, "", timestamp, reader)
}

func (h handler) putObject(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	repo := vars["repo"]
	branch := vars["branch"]
	file := vars["file"]

	expectedHash := r.Header.Get("Content-MD5")

	if expectedHash != "" {
		expectedHashBytes, err := base64.StdEncoding.DecodeString(expectedHash)

		if err != nil {
			writeBadRequest(w, fmt.Errorf("could not decode `Content-MD5`, as it is not base64-encoded"))
			return
		}

		h.putObjectVerifying(w, r, repo, branch, file, expectedHashBytes)
		return
	}

	h.putObjectUnverified(w, r, repo, branch, file)
}

func (h handler) putObjectVerifying(w http.ResponseWriter, r *http.Request, repo, branch, file string, expectedHash []byte) {
	hasher := md5.New()
	reader := io.TeeReader(r.Body, hasher)

	_, err := h.pc.PutFileOverwrite(repo, branch, file, reader, 0)
	if err != nil {
		// the error may be because the repo or branch does not exist -
		// double-check that by inspecting the branch, so we can serve a 404
		// instead
		_, inspectError := h.pc.InspectBranch(repo, branch)
		writeMaybeNotFound(w, inspectError)
		return
	}

	actualHash := hasher.Sum(nil)

	if !bytes.Equal(expectedHash, actualHash) {
		err = fmt.Errorf("content checksums differ; expected=%x, actual=%x", expectedHash, actualHash)
		writeServerError(w, err)
		return
	}

	w.Write([]byte{})
}

func (h handler) putObjectUnverified(w http.ResponseWriter, r *http.Request, repo, branch, file string) {
	_, err := h.pc.PutFileOverwrite(repo, branch, file, r.Body, 0)
	if err != nil {
		// the error may be because the repo or branch does not exist -
		// double-check that by inspecting the branch, so we can serve a 404
		// instead
		_, inspectError := h.pc.InspectBranch(repo, branch)
		writeMaybeNotFound(w, inspectError)
		return
	}

	w.Write([]byte{})
}

// Server runs an HTTP server with an S3-like API for PFS. This allows you to
// use s3 clients to acccess PFS contents.
//
// This returns an `http.Server` instance. It is the responsibility of the
// caller to start the server. This also makes it possible for the caller to
// enable graceful shutdown if desired; see the `http` package for details.
//
// Bucket names correspond to repo names, and files are accessible via the s3
// key pattern "<branch>/<filepath>". For example, to get the file "a/b/c.txt"
// on the "foo" repo's "master" branch, you'd making an s3 get request with
// bucket = "foo", key = "master/a/b/c.txt".
//
// Note: in s3, bucket names are constrained by IETF RFC 1123, (and its
// predecessor RFC 952) but pachyderm's repo naming constraints are slightly
// more liberal. If the s3 client does any kind of bucket name validation
// (this includes minio), repos whose names do not comply with RFC 1123 will
// not be accessible.
//
// Note: In `s3cmd`, you must set the access key and secret key, even though
// this API will ignore them - otherwise, you'll get an opaque config error:
// https://github.com/s3tools/s3cmd/issues/845#issuecomment-464885959
func Server(pc *client.APIClient, port uint16) *http.Server {
	handler := newHandler(pc)

	// repo validation regex is the same as minio
	router := mux.NewRouter()
	router.HandleFunc(`/`, handler.root).Methods("GET")
	router.HandleFunc(`/{repo:[a-z0-9][a-z0-9\.\-]{1,61}[a-z0-9]}/`, handler.repo).Methods("GET", "PUT")
	router.HandleFunc(`/{repo:[a-z0-9][a-z0-9\.\-]{1,61}[a-z0-9]}/{branch}/{file:.+}`, handler.getObject).Methods("GET")
	router.HandleFunc(`/{repo:[a-z0-9][a-z0-9\.\-]{1,61}[a-z0-9]}/{branch}/{file:.+}`, handler.putObject).Methods("PUT")
	router.HandleFunc(`/_ping`, handler.ping).Methods("GET")

	// Note: error log is not customized on this `http.Server`, which means
	// it'll default to using the stdlib logger and produce log messages that
	// don't look like the ones produced elsewhere, and aren't configured
	// properly. In testing, this didn't seem to be a big deal because it's
	// rather hard to trigger it anyways, but if we find a reliable way to
	// create error logs, it might be worthwhile to fix.
	return &http.Server{
		Addr: fmt.Sprintf(":%d", port),
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			log.Debugf("s3 gateway request: %s %s", r.Method, r.RequestURI)
			router.ServeHTTP(w, r)
		}),
	}
}
<|endoftext|>


<commit_before>package geo

import "math"

// P is point
type P struct {
	X, Y float64
}

// Intersection finds the intersection of two straight lines (a1x+b1y+c1, a2x+b2y+c2)
func Intersection(a1, b1, c1, a2, b2, c2 float64) (float64, float64) {
	return (b1*c2 - b2*c1) / (a1*b2 - a2*b1), (-a1*c2 + a2*c1) / (a1*b2 - a2*b1)
}

// Distance obtains the distance between the point and the line
func Distance(a, b, c, x, y float64) float64 {
	return math.Abs(a*x+b*y+c) / math.Sqrt(a*a+b*b)
}

// DistanceLS determine the distance between the point and the line segment
func DistanceLS(p P, a, b P) float64 {
	if Dot(b.X-a.X, b.Y-a.Y, 0, p.X-a.X, p.Y-a.Y, 0) < 0 {
		return math.Hypot(p.X-a.X, p.Y-a.Y)
	}

	if Dot(a.X-b.X, a.Y-b.Y, 0, p.X-b.X, p.Y-b.Y, 0) < 0 {
		return math.Hypot(p.X-b.X, p.Y-b.Y)
	}

	x, y, z := Cross(b.X-a.X, b.Y-a.Y, 0, p.X-a.X, p.Y-a.Y, 0)
	return math.Sqrt(x*x+y*y+z*z) / math.Hypot(b.X-a.X, b.Y-a.Y)
}

// Dot is inner product
func Dot(x1, y1, z1, x2, y2, z2 float64) float64 {
	return x1*x2 + y1*y2 + z1*z2
}

// Cross is outer product
func Cross(x1, y1, z1, x2, y2, z2 float64) (float64, float64, float64) {
	return y1*z2 - z1*y2, z1*x2 - x1*z2, x1*y2 - y1*x2
}

// IsOrthogonal determine whether two line segments are perpendicular to each other
func IsOrthogonal(a1, a2, b1, b2 P) bool {
	return Dot(a1.X-a2.X, a1.Y-a2.Y, 0, b1.X-b2.X, b1.Y-b2.Y, 0) == 0
}

// IsParallel tests whether parallel two line segments
func IsParallel(a1, a2, b1, b2 P) bool {
	x, y, z := Cross(a1.X-a2.X, a1.Y-a2.Y, 0, b1.X-b2.X, b1.Y-b2.Y, 0)
	return x == 0 && y == 0 && z == 0
}

// IsIntersection determine whether two line segments intersect
func IsIntersection(a1, a2, b1, b2 P) bool {
	ta := (b1.X-b2.X)*(a1.Y-b1.Y) + (b1.Y-b2.Y)*(b1.X-a1.X)
	tb := (b1.X-b2.X)*(a2.Y-b1.Y) + (b1.Y-b2.Y)*(b1.X-a2.X)
	tc := (a1.X-a2.X)*(b1.Y-a1.Y) + (a1.Y-a2.Y)*(a1.X-b1.X)
	td := (a1.X-a2.X)*(b2.Y-a1.Y) + (a1.Y-a2.Y)*(a1.X-b2.X)
	return tc*td < 0 && ta*tb < 0
}

// Rotate rotate the origin center
func Rotate(x, y, rad float64) (float64, float64) {
	return x*math.Cos(rad) - y*math.Sin(rad), x*math.Sin(rad) + y*math.Cos(rad)
}

// EquationL is Equation of a line (ax+by+c)
func EquationL(a, b P) (float64, float64, float64) {
	return b.Y - a.Y, a.X - b.X, a.Y*b.X - a.X*b.Y
}

// Area2D to calculate the area of a polygon
func Area2D(ps ...[]float64) float64 {
	sum := float64(0)
	ps = append(ps, []float64{ps[0][0], ps[0][1]})

	for i, v := range ps[:len(ps)-1] {
		x, y := v[0]-ps[i+1][0], v[1]+ps[i+1][1]
		sum += x * y
	}
	return math.Abs(sum) / 2
}

// PosL is tests whether a point is on the right or left side of the line segment
func PosL(p P, a, b P) int {
	x1, y1 := b.X-a.X, b.Y-a.Y
	x2, y2 := p.X-a.X, p.Y-a.Y
	c := x1*y2 - y1*x2
	switch {
	case c > 0:
		return 1 // left
	case c < 0:
		return -1 // right
	default:
		return 0 // on the line
	}
}

// InPolygon to the inside and outside judgment of a convex polygon
func InPolygon(p P, ps []P) bool {
	ps = append(ps, ps[0])
	sign := PosL(p, ps[0], ps[1])
	for i, v := range ps[1 : len(ps)-1] {
		if sign*PosL(p, v, ps[i+2]) < 0 {
			return false
		}
	}
	return true
}

// NearestP determmine the nearest point of the line segment from point
func NearestP(p P, a, b P) P {
	if Dot(b.X-a.X, b.Y-a.Y, 0, p.X-a.X, p.Y-a.Y, 0) < 0 {
		return a
	}

	if Dot(a.X-b.X, a.Y-b.Y, 0, p.X-b.X, p.Y-b.Y, 0) < 0 {
		return b
	}
	ba := P{b.X - a.X, b.Y - a.Y}
	s := Dot(p.X-a.X, p.Y-a.Y, 0, ba.X, ba.Y, 0) / (ba.X*ba.X + ba.Y*ba.Y)
	return P{a.X + s*ba.X, a.Y + s*ba.Y}
}

// SMCircle is smallest enclosing circle
// http://eomole.hatenablog.com/entry/20100219/1292052417
func SMCircle(ps []P) (P, float64) {
	p, index := ps[0], 0
	ratio, r := 0.5, 0.0

	for c := 2 * math.Log2(1000000000); c >= 0; c-- {
		for j := 0; j < 100; j++ {
			max := 0.0
			for i, v := range ps {
				d := math.Hypot(v.X-p.X, v.Y-p.Y)
				if d > max {
					max, index = d, i
				}
			}
			p.X += (ps[index].X - p.X) * ratio
			p.Y += (ps[index].Y - p.Y) * ratio
			r = max
		}
		ratio /= 2
	}
	return p, r
}
<commit_msg>Add Rad, Deg, Angle<commit_after>package geo

import "math"

// P is point
type P struct {
	X, Y float64
}

// Intersection finds the intersection of two straight lines (a1x+b1y+c1, a2x+b2y+c2)
func Intersection(a1, b1, c1, a2, b2, c2 float64) (float64, float64) {
	return (b1*c2 - b2*c1) / (a1*b2 - a2*b1), (-a1*c2 + a2*c1) / (a1*b2 - a2*b1)
}

// Distance obtains the distance between the point and the line
func Distance(a, b, c, x, y float64) float64 {
	return math.Abs(a*x+b*y+c) / math.Sqrt(a*a+b*b)
}

// DistanceLS determine the distance between the point and the line segment
func DistanceLS(p P, a, b P) float64 {
	if Dot(b.X-a.X, b.Y-a.Y, 0, p.X-a.X, p.Y-a.Y, 0) < 0 {
		return math.Hypot(p.X-a.X, p.Y-a.Y)
	}

	if Dot(a.X-b.X, a.Y-b.Y, 0, p.X-b.X, p.Y-b.Y, 0) < 0 {
		return math.Hypot(p.X-b.X, p.Y-b.Y)
	}

	x, y, z := Cross(b.X-a.X, b.Y-a.Y, 0, p.X-a.X, p.Y-a.Y, 0)
	return math.Sqrt(x*x+y*y+z*z) / math.Hypot(b.X-a.X, b.Y-a.Y)
}

// Dot is inner product
func Dot(x1, y1, z1, x2, y2, z2 float64) float64 {
	return x1*x2 + y1*y2 + z1*z2
}

// Cross is outer product
func Cross(x1, y1, z1, x2, y2, z2 float64) (float64, float64, float64) {
	return y1*z2 - z1*y2, z1*x2 - x1*z2, x1*y2 - y1*x2
}

// IsOrthogonal determine whether two line segments are perpendicular to each other
func IsOrthogonal(a1, a2, b1, b2 P) bool {
	return Dot(a1.X-a2.X, a1.Y-a2.Y, 0, b1.X-b2.X, b1.Y-b2.Y, 0) == 0
}

// IsParallel tests whether parallel two line segments
func IsParallel(a1, a2, b1, b2 P) bool {
	x, y, z := Cross(a1.X-a2.X, a1.Y-a2.Y, 0, b1.X-b2.X, b1.Y-b2.Y, 0)
	return x == 0 && y == 0 && z == 0
}

// IsIntersection determine whether two line segments intersect
func IsIntersection(a1, a2, b1, b2 P) bool {
	ta := (b1.X-b2.X)*(a1.Y-b1.Y) + (b1.Y-b2.Y)*(b1.X-a1.X)
	tb := (b1.X-b2.X)*(a2.Y-b1.Y) + (b1.Y-b2.Y)*(b1.X-a2.X)
	tc := (a1.X-a2.X)*(b1.Y-a1.Y) + (a1.Y-a2.Y)*(a1.X-b1.X)
	td := (a1.X-a2.X)*(b2.Y-a1.Y) + (a1.Y-a2.Y)*(a1.X-b2.X)
	return tc*td < 0 && ta*tb < 0
}

// Rotate rotate the origin center
func Rotate(x, y, rad float64) (float64, float64) {
	return x*math.Cos(rad) - y*math.Sin(rad), x*math.Sin(rad) + y*math.Cos(rad)
}

// EquationL is Equation of a line (ax+by+c)
func EquationL(a, b P) (float64, float64, float64) {
	return b.Y - a.Y, a.X - b.X, a.Y*b.X - a.X*b.Y
}

// Area2D to calculate the area of a polygon
func Area2D(ps ...[]float64) float64 {
	sum := float64(0)
	ps = append(ps, []float64{ps[0][0], ps[0][1]})

	for i, v := range ps[:len(ps)-1] {
		x, y := v[0]-ps[i+1][0], v[1]+ps[i+1][1]
		sum += x * y
	}
	return math.Abs(sum) / 2
}

// PosL is tests whether a point is on the right or left side of the line segment
func PosL(p P, a, b P) int {
	x1, y1 := b.X-a.X, b.Y-a.Y
	x2, y2 := p.X-a.X, p.Y-a.Y
	c := x1*y2 - y1*x2
	switch {
	case c > 0:
		return 1 // left
	case c < 0:
		return -1 // right
	default:
		return 0 // on the line
	}
}

// InPolygon to the inside and outside judgment of a convex polygon
func InPolygon(p P, ps []P) bool {
	ps = append(ps, ps[0])
	sign := PosL(p, ps[0], ps[1])
	for i, v := range ps[1 : len(ps)-1] {
		if sign*PosL(p, v, ps[i+2]) < 0 {
			return false
		}
	}
	return true
}

// NearestP determmine the nearest point of the line segment from point
func NearestP(p P, a, b P) P {
	if Dot(b.X-a.X, b.Y-a.Y, 0, p.X-a.X, p.Y-a.Y, 0) < 0 {
		return a
	}

	if Dot(a.X-b.X, a.Y-b.Y, 0, p.X-b.X, p.Y-b.Y, 0) < 0 {
		return b
	}
	ba := P{b.X - a.X, b.Y - a.Y}
	s := Dot(p.X-a.X, p.Y-a.Y, 0, ba.X, ba.Y, 0) / (ba.X*ba.X + ba.Y*ba.Y)
	return P{a.X + s*ba.X, a.Y + s*ba.Y}
}

// SMCircle is smallest enclosing circle
// http://eomole.hatenablog.com/entry/20100219/1292052417
func SMCircle(ps []P) (P, float64) {
	p, index := ps[0], 0
	ratio, r := 0.5, 0.0

	for c := 2 * math.Log2(1000000000); c >= 0; c-- {
		for j := 0; j < 100; j++ {
			max := 0.0
			for i, v := range ps {
				d := math.Hypot(v.X-p.X, v.Y-p.Y)
				if d > max {
					max, index = d, i
				}
			}
			p.X += (ps[index].X - p.X) * ratio
			p.Y += (ps[index].Y - p.Y) * ratio
			r = max
		}
		ratio /= 2
	}
	return p, r
}

// Rad converts from the degree to the radian
func Rad(d float64) float64 {
	return d * math.Pi / 180
}

// Deg converts from the radian to the degree
func Deg(r float64) float64 {
	return r * 180 / math.Pi
}

// Angle determine the angle(radian) of two vectors
func Angle(u, v P) float64 {
	l := math.Hypot(u.X, u.Y) * math.Hypot(v.X, v.Y)
	cos := Dot(u.X, u.Y, 0, v.X, v.Y, 0)
	return math.Acos(cos / l)
}
<|endoftext|>


<commit_before>// errchk $G -e $D/$F.go

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

// check for import conflicts

package main

import "bufio"	// GCCGO_ERROR "previous"
import bufio "os"	// ERROR "redeclared|redefinition|incompatible"

import (
	"fmt";	// GCCGO_ERROR "previous"
	fmt "math";	// ERROR "redeclared|redefinition|incompatible"
)
<commit_msg>Match gccgo error messages.<commit_after>// errchk $G -e $D/$F.go

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

// check for import conflicts

package main

import "bufio"	// GCCGO_ERROR "previous|not used"
import bufio "os"	// ERROR "redeclared|redefinition|incompatible"

import (
	"fmt";	// GCCGO_ERROR "previous|not used"
	fmt "math";	// ERROR "redeclared|redefinition|incompatible"
)
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package node

import (
	"fmt"

	v1 "k8s.io/api/core/v1"
	"k8s.io/api/node/v1beta1"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	runtimeclasstest "k8s.io/kubernetes/pkg/kubelet/runtimeclass/testing"
	"k8s.io/kubernetes/test/e2e/framework"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
	"k8s.io/kubernetes/test/e2e/scheduling"
	imageutils "k8s.io/kubernetes/test/utils/image"
	utilpointer "k8s.io/utils/pointer"

	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
)

var _ = ginkgo.Describe("[sig-node] RuntimeClass", func() {
	f := framework.NewDefaultFramework("runtimeclass")

	ginkgo.It("should reject a Pod requesting a RuntimeClass with conflicting node selector", func() {
		scheduling := &v1beta1.Scheduling{
			NodeSelector: map[string]string{
				"foo": "conflict",
			},
		}

		runtimeClass := newRuntimeClass(f.Namespace.Name, "conflict-runtimeclass")
		runtimeClass.Scheduling = scheduling
		rc, err := f.ClientSet.NodeV1beta1().RuntimeClasses().Create(runtimeClass)
		framework.ExpectNoError(err, "failed to create RuntimeClass resource")

		pod := newRuntimeClassPod(rc.GetName())
		pod.Spec.NodeSelector = map[string]string{
			"foo": "bar",
		}
		_, err = f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(pod)
		framework.ExpectError(err, "should be forbidden")
		gomega.Expect(apierrs.IsForbidden(err)).To(gomega.BeTrue(), "should be forbidden error")
	})

	ginkgo.It("should run a Pod requesting a RuntimeClass with scheduling [NodeFeature:RuntimeHandler] ", func() {
		nodeName := scheduling.GetNodeThatCanRunPod(f)
		nodeSelector := map[string]string{
			"foo":  "bar",
			"fizz": "buzz",
		}
		tolerations := []v1.Toleration{
			{
				Key:      "foo",
				Operator: v1.TolerationOpEqual,
				Value:    "bar",
				Effect:   v1.TaintEffectNoSchedule,
			},
		}
		scheduling := &v1beta1.Scheduling{
			NodeSelector: nodeSelector,
			Tolerations:  tolerations,
		}

		ginkgo.By("Trying to apply a label on the found node.")
		for key, value := range nodeSelector {
			framework.AddOrUpdateLabelOnNode(f.ClientSet, nodeName, key, value)
			framework.ExpectNodeHasLabel(f.ClientSet, nodeName, key, value)
			defer framework.RemoveLabelOffNode(f.ClientSet, nodeName, key)
		}

		ginkgo.By("Trying to apply taint on the found node.")
		taint := v1.Taint{
			Key:    "foo",
			Value:  "bar",
			Effect: v1.TaintEffectNoSchedule,
		}
		framework.AddOrUpdateTaintOnNode(f.ClientSet, nodeName, taint)
		framework.ExpectNodeHasTaint(f.ClientSet, nodeName, &taint)
		defer framework.RemoveTaintOffNode(f.ClientSet, nodeName, taint)

		ginkgo.By("Trying to create runtimeclass and pod")
		runtimeClass := newRuntimeClass(f.Namespace.Name, "non-conflict-runtimeclass")
		runtimeClass.Scheduling = scheduling
		rc, err := f.ClientSet.NodeV1beta1().RuntimeClasses().Create(runtimeClass)
		framework.ExpectNoError(err, "failed to create RuntimeClass resource")

		pod := newRuntimeClassPod(rc.GetName())
		pod.Spec.NodeSelector = map[string]string{
			"foo": "bar",
		}
		pod = f.PodClient().Create(pod)

		framework.ExpectNoError(e2epod.WaitForPodNotPending(f.ClientSet, f.Namespace.Name, pod.Name))

		// check that pod got scheduled on specified node.
		scheduledPod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Get(pod.Name, metav1.GetOptions{})
		framework.ExpectNoError(err)
		framework.ExpectEqual(nodeName, scheduledPod.Spec.NodeName)
		framework.ExpectEqual(nodeSelector, pod.Spec.NodeSelector)
		gomega.Expect(pod.Spec.Tolerations).To(gomega.ContainElement(tolerations[0]))
	})
})

// newRuntimeClass returns a test runtime class.
func newRuntimeClass(namespace, name string) *v1beta1.RuntimeClass {
	uniqueName := fmt.Sprintf("%s-%s", namespace, name)
	return runtimeclasstest.NewRuntimeClass(uniqueName, framework.PreconfiguredRuntimeClassHandler())
}

// newRuntimeClassPod returns a test pod with the given runtimeClassName.
func newRuntimeClassPod(runtimeClassName string) *v1.Pod {
	return &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: fmt.Sprintf("test-runtimeclass-%s-", runtimeClassName),
		},
		Spec: v1.PodSpec{
			RuntimeClassName: &runtimeClassName,
			Containers: []v1.Container{{
				Name:    "test",
				Image:   imageutils.GetE2EImage(imageutils.BusyBox),
				Command: []string{"true"},
			}},
			RestartPolicy:                v1.RestartPolicyNever,
			AutomountServiceAccountToken: utilpointer.BoolPtr(false),
		},
	}
}
<commit_msg>tag test that taints a node as disruptive<commit_after>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package node

import (
	"fmt"

	v1 "k8s.io/api/core/v1"
	"k8s.io/api/node/v1beta1"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	runtimeclasstest "k8s.io/kubernetes/pkg/kubelet/runtimeclass/testing"
	"k8s.io/kubernetes/test/e2e/framework"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
	"k8s.io/kubernetes/test/e2e/scheduling"
	imageutils "k8s.io/kubernetes/test/utils/image"
	utilpointer "k8s.io/utils/pointer"

	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
)

var _ = ginkgo.Describe("[sig-node] RuntimeClass", func() {
	f := framework.NewDefaultFramework("runtimeclass")

	ginkgo.It("should reject a Pod requesting a RuntimeClass with conflicting node selector", func() {
		scheduling := &v1beta1.Scheduling{
			NodeSelector: map[string]string{
				"foo": "conflict",
			},
		}

		runtimeClass := newRuntimeClass(f.Namespace.Name, "conflict-runtimeclass")
		runtimeClass.Scheduling = scheduling
		rc, err := f.ClientSet.NodeV1beta1().RuntimeClasses().Create(runtimeClass)
		framework.ExpectNoError(err, "failed to create RuntimeClass resource")

		pod := newRuntimeClassPod(rc.GetName())
		pod.Spec.NodeSelector = map[string]string{
			"foo": "bar",
		}
		_, err = f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(pod)
		framework.ExpectError(err, "should be forbidden")
		gomega.Expect(apierrs.IsForbidden(err)).To(gomega.BeTrue(), "should be forbidden error")
	})

	ginkgo.It("should run a Pod requesting a RuntimeClass with scheduling [NodeFeature:RuntimeHandler] [Disruptive] ", func() {
		nodeName := scheduling.GetNodeThatCanRunPod(f)
		nodeSelector := map[string]string{
			"foo":  "bar",
			"fizz": "buzz",
		}
		tolerations := []v1.Toleration{
			{
				Key:      "foo",
				Operator: v1.TolerationOpEqual,
				Value:    "bar",
				Effect:   v1.TaintEffectNoSchedule,
			},
		}
		scheduling := &v1beta1.Scheduling{
			NodeSelector: nodeSelector,
			Tolerations:  tolerations,
		}

		ginkgo.By("Trying to apply a label on the found node.")
		for key, value := range nodeSelector {
			framework.AddOrUpdateLabelOnNode(f.ClientSet, nodeName, key, value)
			framework.ExpectNodeHasLabel(f.ClientSet, nodeName, key, value)
			defer framework.RemoveLabelOffNode(f.ClientSet, nodeName, key)
		}

		ginkgo.By("Trying to apply taint on the found node.")
		taint := v1.Taint{
			Key:    "foo",
			Value:  "bar",
			Effect: v1.TaintEffectNoSchedule,
		}
		framework.AddOrUpdateTaintOnNode(f.ClientSet, nodeName, taint)
		framework.ExpectNodeHasTaint(f.ClientSet, nodeName, &taint)
		defer framework.RemoveTaintOffNode(f.ClientSet, nodeName, taint)

		ginkgo.By("Trying to create runtimeclass and pod")
		runtimeClass := newRuntimeClass(f.Namespace.Name, "non-conflict-runtimeclass")
		runtimeClass.Scheduling = scheduling
		rc, err := f.ClientSet.NodeV1beta1().RuntimeClasses().Create(runtimeClass)
		framework.ExpectNoError(err, "failed to create RuntimeClass resource")

		pod := newRuntimeClassPod(rc.GetName())
		pod.Spec.NodeSelector = map[string]string{
			"foo": "bar",
		}
		pod = f.PodClient().Create(pod)

		framework.ExpectNoError(e2epod.WaitForPodNotPending(f.ClientSet, f.Namespace.Name, pod.Name))

		// check that pod got scheduled on specified node.
		scheduledPod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Get(pod.Name, metav1.GetOptions{})
		framework.ExpectNoError(err)
		framework.ExpectEqual(nodeName, scheduledPod.Spec.NodeName)
		framework.ExpectEqual(nodeSelector, pod.Spec.NodeSelector)
		gomega.Expect(pod.Spec.Tolerations).To(gomega.ContainElement(tolerations[0]))
	})
})

// newRuntimeClass returns a test runtime class.
func newRuntimeClass(namespace, name string) *v1beta1.RuntimeClass {
	uniqueName := fmt.Sprintf("%s-%s", namespace, name)
	return runtimeclasstest.NewRuntimeClass(uniqueName, framework.PreconfiguredRuntimeClassHandler())
}

// newRuntimeClassPod returns a test pod with the given runtimeClassName.
func newRuntimeClassPod(runtimeClassName string) *v1.Pod {
	return &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: fmt.Sprintf("test-runtimeclass-%s-", runtimeClassName),
		},
		Spec: v1.PodSpec{
			RuntimeClassName: &runtimeClassName,
			Containers: []v1.Container{{
				Name:    "test",
				Image:   imageutils.GetE2EImage(imageutils.BusyBox),
				Command: []string{"true"},
			}},
			RestartPolicy:                v1.RestartPolicyNever,
			AutomountServiceAccountToken: utilpointer.BoolPtr(false),
		},
	}
}
<|endoftext|>


<commit_before>// Package git provides basic git client functionality
package git

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
)

var (
	repoParentDir string
	username      string
	password      string
)

func init() {
	repoParentDir = os.TempDir()
}

func SetParentDir(path string) {
	repoParentDir = path
}

func ParentDir() string {
	return repoParentDir
}

func SetAuth(user string, pwd string) {
	username = user
	password = pwd
}

func GenerateCloneURL(repositoryFullName string) string {
	return fmt.Sprintf("https://%s:%s@github.com/%s.git", username, password, repositoryFullName)
}

func Exists(repositoryPath string) bool {
	_, err := os.Stat(repositoryPath)
	return !os.IsNotExist(err)
}

func GetRepositoryFilePath(name string) string {
	return path.Join(repoParentDir, name)
}

// Clone executes a git clone command on the system. It returns the path to the repository on the system.
func Clone(repositoryUrl string) (string, error) {
	orgName := extractOrgFromURL(repositoryUrl)
	repoName := extractRepoNameFromURL(repositoryUrl)
	repositoryPath := path.Join(repoParentDir, orgName, repoName)

	log.Println("git.clone.started:", repositoryPath)

	cmd := exec.Command("git", "clone", repositoryUrl, repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.clone.failed:", repositoryPath, err.Error())
		return "", err
	}

	log.Println("git.clone.finished:", repositoryPath)

	return repositoryPath, nil
}

// Calls git fetch inside repository path
func Fetch(repositoryPath string) error {
	log.Println("git.fetch.started:", repositoryPath)

	cmd := exec.Command("git", "fetch", "origin")
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.fetch.failed:", repositoryPath, err.Error())
		return err
	}

	log.Println("git.fetch.finished:", repositoryPath)

	return nil
}

// Checks out a given git ref inside repository path
func Checkout(repositoryPath, gitRef string) error {
	log.Println("git.checkout.started:", repositoryPath, gitRef)

	cmd := exec.Command("git", "checkout", gitRef)
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.checkout.failed:", repositoryPath, err.Error())
		return err
	}

	log.Println("git.checkout.finished:", repositoryPath, gitRef)

	return nil
}

// Does hard reset inside repository path
func Reset(repositoryPath, branch string) error {
	log.Println("git.reset.started:", repositoryPath, branch)

	cmd := exec.Command("git", "reset", "--hard", branch)
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.reset.failed:", repositoryPath, err.Error())
		return err
	}

	log.Println("git.reset.finished:", repositoryPath, branch)

	return nil
}

// Rebases branch with baseBranch inside repository path
func Rebase(repositoryPath, baseBranch string) error {
	log.Println("git.rebase.started:", repositoryPath, baseBranch)

	cmd := exec.Command("git", "rebase", baseBranch)
	cmd.Dir = path.Join(repositoryPath)

	if err := cmd.Run(); err != nil {
		log.Printf("git.rebase.abort.started repo: %s, err: %s \n", repositoryPath, err.Error())

		abortCmd := exec.Command("git", "rebase", "--abort")
		abortCmd.Dir = path.Join(repositoryPath)

		if err := abortCmd.Run(); err != nil {
			log.Println("git.rebase.abort.failed:", repositoryPath)
		} else {
			log.Println("git.rebase.abort.finished:", repositoryPath)
		}

		return err
	}

	log.Println("git.rebase.finished:", repositoryPath, baseBranch)

	return nil
}

// Pushes branch back to origin
func Push(repositoryPath, branch string) error {
	log.Println("git.push.started:", repositoryPath, branch)

	cmd := exec.Command("git", "push", "--force", "origin", branch)
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.push.failed:", repositoryPath, err.Error())
		return err
	}

	log.Println("git.push.finished:", repositoryPath, branch)

	return nil
}

func Clean() {
	log.Printf("git.cache.cleanup.started: path: %s\n", repoParentDir)

	if err := os.RemoveAll(repoParentDir); err != nil {
		log.Fatalf("git.cache.cleanup.failed: path: %s, error: %s\n", repoParentDir, err.Error())
	}

	log.Printf("git.cache.cleanup.finished")
}

func extractOrgFromURL(githubURL string) string {
	splitBySlash := strings.Split(githubURL, "/")
	return splitBySlash[len(splitBySlash)-2]
}

func extractRepoNameFromURL(githubURL string) string {
	splitBySlash := strings.Split(githubURL, "/")
	repoNameWithExt := splitBySlash[len(splitBySlash)-1]
	return strings.Split(repoNameWithExt, ".")[0]
}
<commit_msg>Experimental logging for git rebase failures<commit_after>// Package git provides basic git client functionality
package git

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
)

var (
	repoParentDir string
	username      string
	password      string
)

type Output struct {
	Buffer string
}

func (w *Output) Write(b []byte) (int, error) {
	w.Buffer = w.Buffer + string(b)

	return len(b), nil
}

func (o *Output) String() string {
	return o.Buffer
}

func init() {
	repoParentDir = os.TempDir()
}

func SetParentDir(path string) {
	repoParentDir = path
}

func ParentDir() string {
	return repoParentDir
}

func SetAuth(user string, pwd string) {
	username = user
	password = pwd
}

func GenerateCloneURL(repositoryFullName string) string {
	return fmt.Sprintf("https://%s:%s@github.com/%s.git", username, password, repositoryFullName)
}

func Exists(repositoryPath string) bool {
	_, err := os.Stat(repositoryPath)
	return !os.IsNotExist(err)
}

func GetRepositoryFilePath(name string) string {
	return path.Join(repoParentDir, name)
}

// Clone executes a git clone command on the system. It returns the path to the repository on the system.
func Clone(repositoryUrl string) (string, error) {
	orgName := extractOrgFromURL(repositoryUrl)
	repoName := extractRepoNameFromURL(repositoryUrl)
	repositoryPath := path.Join(repoParentDir, orgName, repoName)

	log.Println("git.clone.started:", repositoryPath)

	cmd := exec.Command("git", "clone", repositoryUrl, repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.clone.failed:", repositoryPath, err.Error())
		return "", err
	}

	log.Println("git.clone.finished:", repositoryPath)

	return repositoryPath, nil
}

// Calls git fetch inside repository path
func Fetch(repositoryPath string) error {
	log.Println("git.fetch.started:", repositoryPath)

	cmd := exec.Command("git", "fetch", "origin")
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.fetch.failed:", repositoryPath, err.Error())
		return err
	}

	log.Println("git.fetch.finished:", repositoryPath)

	return nil
}

// Checks out a given git ref inside repository path
func Checkout(repositoryPath, gitRef string) error {
	log.Println("git.checkout.started:", repositoryPath, gitRef)

	cmd := exec.Command("git", "checkout", gitRef)
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.checkout.failed:", repositoryPath, err.Error())
		return err
	}

	log.Println("git.checkout.finished:", repositoryPath, gitRef)

	return nil
}

// Does hard reset inside repository path
func Reset(repositoryPath, branch string) error {
	log.Println("git.reset.started:", repositoryPath, branch)

	cmd := exec.Command("git", "reset", "--hard", branch)
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.reset.failed:", repositoryPath, err.Error())
		return err
	}

	log.Println("git.reset.finished:", repositoryPath, branch)

	return nil
}

// Rebases branch with baseBranch inside repository path
func Rebase(repositoryPath, baseBranch string) error {
	cmdOutput := &Output{Buffer: ""}
	log.Println("git.rebase.started:", repositoryPath, baseBranch)

	cmd := exec.Command("git", "rebase", baseBranch)
	cmd.Dir = path.Join(repositoryPath)
	cmd.Stderr = cmdOutput
	cmd.Stdout = cmdOutput

	if err := cmd.Run(); err != nil {
		log.Printf("git.rebase.failed repo: %s, err: %s \n", repositoryPath, err.Error())

		log.Printf("git.rebase.abort.started repo: %s, err: %s, stderr: %s \n", repositoryPath, err.Error(), cmdOutput.String())

		abortCmd := exec.Command("git", "rebase", "--abort")
		abortCmd.Dir = path.Join(repositoryPath)

		if err := abortCmd.Run(); err != nil {
			log.Println("git.rebase.abort.failed:", repositoryPath)
		} else {
			log.Println("git.rebase.abort.finished:", repositoryPath)
		}

		return err
	}

	log.Println("git.rebase.finished:", repositoryPath, baseBranch)

	return nil
}

// Pushes branch back to origin
func Push(repositoryPath, branch string) error {
	log.Println("git.push.started:", repositoryPath, branch)

	cmd := exec.Command("git", "push", "--force", "origin", branch)
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.push.failed:", repositoryPath, err.Error())
		return err
	}

	log.Println("git.push.finished:", repositoryPath, branch)

	return nil
}

func Clean() {
	log.Printf("git.cache.cleanup.started: path: %s\n", repoParentDir)

	if err := os.RemoveAll(repoParentDir); err != nil {
		log.Fatalf("git.cache.cleanup.failed: path: %s, error: %s\n", repoParentDir, err.Error())
	}

	log.Printf("git.cache.cleanup.finished")
}

func extractOrgFromURL(githubURL string) string {
	splitBySlash := strings.Split(githubURL, "/")
	return splitBySlash[len(splitBySlash)-2]
}

func extractRepoNameFromURL(githubURL string) string {
	splitBySlash := strings.Split(githubURL, "/")
	repoNameWithExt := splitBySlash[len(splitBySlash)-1]
	return strings.Split(repoNameWithExt, ".")[0]
}
<|endoftext|>


<commit_before>package git

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/github/hub/cmd"
)

var GlobalFlags []string

func Version() (string, error) {
	output, err := gitOutput("version")
	if err != nil {
		return "", fmt.Errorf("Can't load git version")
	}

	return output[0], nil
}

func Dir() (string, error) {
	output, err := gitOutput("rev-parse", "-q", "--git-dir")
	if err != nil {
		return "", fmt.Errorf("Not a git repository (or any of the parent directories): .git")
	}

	var chdir string
	for i, flag := range GlobalFlags {
		if flag == "-C" {
			dir := GlobalFlags[i+1]
			if filepath.IsAbs(dir) {
				chdir = dir
			} else {
				chdir = filepath.Join(chdir, dir)
			}
		}
	}

	gitDir := output[0]

	if !filepath.IsAbs(gitDir) {
		if chdir != "" {
			gitDir = filepath.Join(chdir, gitDir)
		}

		gitDir, err = filepath.Abs(gitDir)
		if err != nil {
			return "", err
		}

		gitDir = filepath.Clean(gitDir)
	}

	return gitDir, nil
}

func HasFile(segments ...string) bool {
	// The blessed way to resolve paths within git dir since Git 2.5.0
	output, err := gitOutput("rev-parse", "-q", "--git-path", filepath.Join(segments...))
	if err == nil && output[0] != "--git-path" {
		if _, err := os.Stat(output[0]); err == nil {
			return true
		}
	}

	// Fallback for older git versions
	dir, err := Dir()
	if err != nil {
		return false
	}

	s := []string{dir}
	s = append(s, segments...)
	path := filepath.Join(s...)
	if _, err := os.Stat(path); err == nil {
		return true
	}

	return false
}

func BranchAtRef(paths ...string) (name string, err error) {
	dir, err := Dir()
	if err != nil {
		return
	}

	segments := []string{dir}
	segments = append(segments, paths...)
	path := filepath.Join(segments...)
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return
	}

	n := string(b)
	refPrefix := "ref: "
	if strings.HasPrefix(n, refPrefix) {
		name = strings.TrimPrefix(n, refPrefix)
		name = strings.TrimSpace(name)
	} else {
		err = fmt.Errorf("No branch info in %s: %s", path, n)
	}

	return
}

func Editor() (string, error) {
	output, err := gitOutput("var", "GIT_EDITOR")
	if err != nil {
		return "", fmt.Errorf("Can't load git var: GIT_EDITOR")
	}

	return output[0], nil
}

func Head() (string, error) {
	return BranchAtRef("HEAD")
}

func SymbolicFullName(name string) (string, error) {
	output, err := gitOutput("rev-parse", "--symbolic-full-name", name)
	if err != nil {
		return "", fmt.Errorf("Unknown revision or path not in the working tree: %s", name)
	}

	return output[0], nil
}

func Ref(ref string) (string, error) {
	output, err := gitOutput("rev-parse", "-q", ref)
	if err != nil {
		return "", fmt.Errorf("Unknown revision or path not in the working tree: %s", ref)
	}

	return output[0], nil
}

func RefList(a, b string) ([]string, error) {
	ref := fmt.Sprintf("%s...%s", a, b)
	output, err := gitOutput("rev-list", "--cherry-pick", "--right-only", "--no-merges", ref)
	if err != nil {
		return []string{}, fmt.Errorf("Can't load rev-list for %s", ref)
	}

	return output, nil
}

func CommentChar() string {
	char, err := Config("core.commentchar")
	if err != nil {
		char = "#"
	}

	return char
}

func Show(sha string) (string, error) {
	cmd := cmd.New("git")
	cmd.WithArg("show").WithArg("-s").WithArg("--format=%s%n%+b").WithArg(sha)

	output, err := cmd.CombinedOutput()
	output = strings.TrimSpace(output)

	return output, err
}

func Log(sha1, sha2 string) (string, error) {
	execCmd := cmd.New("git")
	execCmd.WithArg("log").WithArg("--no-color")
	execCmd.WithArg("--format=%h (%aN, %ar)%n%w(78,3,3)%s%n%+b")
	execCmd.WithArg("--cherry")
	shaRange := fmt.Sprintf("%s...%s", sha1, sha2)
	execCmd.WithArg(shaRange)

	outputs, err := execCmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("Can't load git log %s..%s", sha1, sha2)
	}

	return outputs, nil
}

func Remotes() ([]string, error) {
	return gitOutput("remote", "-v")
}

func Config(name string) (string, error) {
	return gitGetConfig(name)
}

func GlobalConfig(name string) (string, error) {
	return gitGetConfig("--global", name)
}

func SetGlobalConfig(name, value string) error {
	_, err := gitConfig("--global", name, value)
	return err
}

func gitGetConfig(args ...string) (string, error) {
	output, err := gitOutput(gitConfigCommand(args)...)
	if err != nil {
		return "", fmt.Errorf("Unknown config %s", args[len(args)-1])
	}

	if len(output) == 0 {
		return "", nil
	}

	return output[0], nil
}

func gitConfig(args ...string) ([]string, error) {
	return gitOutput(gitConfigCommand(args)...)
}

func gitConfigCommand(args []string) []string {
	cmd := []string{"config"}
	return append(cmd, args...)
}

func Alias(name string) (string, error) {
	return Config(fmt.Sprintf("alias.%s", name))
}

func Run(command string, args ...string) error {
	cmd := cmd.New("git")

	for _, v := range GlobalFlags {
		cmd.WithArg(v)
	}

	cmd.WithArg(command)

	for _, a := range args {
		cmd.WithArg(a)
	}

	return cmd.Run()
}

func gitOutput(input ...string) (outputs []string, err error) {
	cmd := cmd.New("git")

	for _, v := range GlobalFlags {
		cmd.WithArg(v)
	}

	for _, i := range input {
		cmd.WithArg(i)
	}

	out, err := cmd.CombinedOutput()
	for _, line := range strings.Split(out, "\n") {
		line = strings.TrimSpace(line)
		if line != "" {
			outputs = append(outputs, string(line))
		}
	}

	return outputs, err
}
<commit_msg>Cache result of `git rev-parse --git-dir`<commit_after>package git

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/github/hub/cmd"
)

var GlobalFlags []string

func Version() (string, error) {
	output, err := gitOutput("version")
	if err != nil {
		return "", fmt.Errorf("Can't load git version")
	}

	return output[0], nil
}

var cachedDir string

func Dir() (string, error) {
	if cachedDir != "" {
		return cachedDir, nil
	}

	output, err := gitOutput("rev-parse", "-q", "--git-dir")
	if err != nil {
		return "", fmt.Errorf("Not a git repository (or any of the parent directories): .git")
	}

	var chdir string
	for i, flag := range GlobalFlags {
		if flag == "-C" {
			dir := GlobalFlags[i+1]
			if filepath.IsAbs(dir) {
				chdir = dir
			} else {
				chdir = filepath.Join(chdir, dir)
			}
		}
	}

	gitDir := output[0]

	if !filepath.IsAbs(gitDir) {
		if chdir != "" {
			gitDir = filepath.Join(chdir, gitDir)
		}

		gitDir, err = filepath.Abs(gitDir)
		if err != nil {
			return "", err
		}

		gitDir = filepath.Clean(gitDir)
	}

	cachedDir = gitDir
	return gitDir, nil
}

func HasFile(segments ...string) bool {
	// The blessed way to resolve paths within git dir since Git 2.5.0
	output, err := gitOutput("rev-parse", "-q", "--git-path", filepath.Join(segments...))
	if err == nil && output[0] != "--git-path" {
		if _, err := os.Stat(output[0]); err == nil {
			return true
		}
	}

	// Fallback for older git versions
	dir, err := Dir()
	if err != nil {
		return false
	}

	s := []string{dir}
	s = append(s, segments...)
	path := filepath.Join(s...)
	if _, err := os.Stat(path); err == nil {
		return true
	}

	return false
}

func BranchAtRef(paths ...string) (name string, err error) {
	dir, err := Dir()
	if err != nil {
		return
	}

	segments := []string{dir}
	segments = append(segments, paths...)
	path := filepath.Join(segments...)
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return
	}

	n := string(b)
	refPrefix := "ref: "
	if strings.HasPrefix(n, refPrefix) {
		name = strings.TrimPrefix(n, refPrefix)
		name = strings.TrimSpace(name)
	} else {
		err = fmt.Errorf("No branch info in %s: %s", path, n)
	}

	return
}

func Editor() (string, error) {
	output, err := gitOutput("var", "GIT_EDITOR")
	if err != nil {
		return "", fmt.Errorf("Can't load git var: GIT_EDITOR")
	}

	return output[0], nil
}

func Head() (string, error) {
	return BranchAtRef("HEAD")
}

func SymbolicFullName(name string) (string, error) {
	output, err := gitOutput("rev-parse", "--symbolic-full-name", name)
	if err != nil {
		return "", fmt.Errorf("Unknown revision or path not in the working tree: %s", name)
	}

	return output[0], nil
}

func Ref(ref string) (string, error) {
	output, err := gitOutput("rev-parse", "-q", ref)
	if err != nil {
		return "", fmt.Errorf("Unknown revision or path not in the working tree: %s", ref)
	}

	return output[0], nil
}

func RefList(a, b string) ([]string, error) {
	ref := fmt.Sprintf("%s...%s", a, b)
	output, err := gitOutput("rev-list", "--cherry-pick", "--right-only", "--no-merges", ref)
	if err != nil {
		return []string{}, fmt.Errorf("Can't load rev-list for %s", ref)
	}

	return output, nil
}

func CommentChar() string {
	char, err := Config("core.commentchar")
	if err != nil {
		char = "#"
	}

	return char
}

func Show(sha string) (string, error) {
	cmd := cmd.New("git")
	cmd.WithArg("show").WithArg("-s").WithArg("--format=%s%n%+b").WithArg(sha)

	output, err := cmd.CombinedOutput()
	output = strings.TrimSpace(output)

	return output, err
}

func Log(sha1, sha2 string) (string, error) {
	execCmd := cmd.New("git")
	execCmd.WithArg("log").WithArg("--no-color")
	execCmd.WithArg("--format=%h (%aN, %ar)%n%w(78,3,3)%s%n%+b")
	execCmd.WithArg("--cherry")
	shaRange := fmt.Sprintf("%s...%s", sha1, sha2)
	execCmd.WithArg(shaRange)

	outputs, err := execCmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("Can't load git log %s..%s", sha1, sha2)
	}

	return outputs, nil
}

func Remotes() ([]string, error) {
	return gitOutput("remote", "-v")
}

func Config(name string) (string, error) {
	return gitGetConfig(name)
}

func GlobalConfig(name string) (string, error) {
	return gitGetConfig("--global", name)
}

func SetGlobalConfig(name, value string) error {
	_, err := gitConfig("--global", name, value)
	return err
}

func gitGetConfig(args ...string) (string, error) {
	output, err := gitOutput(gitConfigCommand(args)...)
	if err != nil {
		return "", fmt.Errorf("Unknown config %s", args[len(args)-1])
	}

	if len(output) == 0 {
		return "", nil
	}

	return output[0], nil
}

func gitConfig(args ...string) ([]string, error) {
	return gitOutput(gitConfigCommand(args)...)
}

func gitConfigCommand(args []string) []string {
	cmd := []string{"config"}
	return append(cmd, args...)
}

func Alias(name string) (string, error) {
	return Config(fmt.Sprintf("alias.%s", name))
}

func Run(command string, args ...string) error {
	cmd := cmd.New("git")

	for _, v := range GlobalFlags {
		cmd.WithArg(v)
	}

	cmd.WithArg(command)

	for _, a := range args {
		cmd.WithArg(a)
	}

	return cmd.Run()
}

func gitOutput(input ...string) (outputs []string, err error) {
	cmd := cmd.New("git")

	for _, v := range GlobalFlags {
		cmd.WithArg(v)
	}

	for _, i := range input {
		cmd.WithArg(i)
	}

	out, err := cmd.CombinedOutput()
	for _, line := range strings.Split(out, "\n") {
		line = strings.TrimSpace(line)
		if line != "" {
			outputs = append(outputs, string(line))
		}
	}

	return outputs, err
}
<|endoftext|>