<commit_before>package parser

import (
	"fmt"
	"strconv"

	"github.com/goby-lang/goby/ast"
	"github.com/goby-lang/goby/token"
)

var precedence = map[token.Type]int{
	token.Eq:                 EQUALS,
	token.NotEq:              EQUALS,
	token.LT:                 LESSGREATER,
	token.LTE:                LESSGREATER,
	token.GT:                 LESSGREATER,
	token.GTE:                LESSGREATER,
	token.COMP:               LESSGREATER,
	token.And:                LOGIC,
	token.Or:                 LOGIC,
	token.Plus:               SUM,
	token.Minus:              SUM,
	token.Incr:               SUM,
	token.Decr:               SUM,
	token.Modulo:             SUM,
	token.Assign:             ASSIGN,
	token.Slash:              PRODUCT,
	token.Asterisk:           PRODUCT,
	token.Pow:                PRODUCT,
	token.LBracket:           INDEX,
	token.Dot:                CALL,
	token.LParen:             CALL,
	token.ResolutionOperator: CALL,
}

// Constants for denoting precedence
const (
	_ int = iota
	LOWEST
	LOGIC
	EQUALS
	LESSGREATER
	ASSIGN
	SUM
	PRODUCT
	PREFIX
	INDEX
	CALL
)

type (
	prefixParseFn func() ast.Expression
	infixParseFn  func(ast.Expression) ast.Expression
)

func (p *Parser) parseExpression(precedence int) ast.Expression {
	prefix := p.prefixParseFns[p.curToken.Type]
	if prefix == nil {
		p.noPrefixParseFnError(p.curToken.Type)
		return nil
	}

	leftExp := prefix()

	for !p.peekTokenIs(token.Semicolon) && precedence < p.peekPrecedence() && p.peekTokenAtSameLine() {

		infix := p.infixParseFns[p.peekToken.Type]
		if (p.peekTokenIs(token.Int) || p.peekTokenIs(token.Ident) || p.peekTokenIs(token.InstanceVariable)) && precedence == 0 { // method call without paren case
			infix = p.parseCallExpression
		}

		if infix == nil {
			return leftExp
		}
		p.nextToken()
		leftExp = infix(leftExp)
	}

	return leftExp
}

func (p *Parser) parseSelfExpression() ast.Expression {
	return &ast.SelfExpression{Token: p.curToken}
}

func (p *Parser) parseIdentifier() ast.Expression {
	return &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
}

func (p *Parser) parseConstant() ast.Expression {
	c := &ast.Constant{Token: p.curToken, Value: p.curToken.Literal}

	if p.peekTokenIs(token.ResolutionOperator) {
		p.nextToken()
		return p.parseInfixExpression(c)
	}

	return c
}

func (p *Parser) parseInstanceVariable() ast.Expression {
	return &ast.InstanceVariable{Token: p.curToken, Value: p.curToken.Literal}
}

func (p *Parser) parseIntegerLiteral() ast.Expression {
	lit := &ast.IntegerLiteral{Token: p.curToken}

	value, err := strconv.ParseInt(lit.TokenLiteral(), 0, 64)
	if err != nil {
		msg := fmt.Sprintf("could not parse %q as integer", lit.TokenLiteral())
		p.errors = append(p.errors, msg)
		return nil
	}

	lit.Value = int(value)

	return lit
}

func (p *Parser) parseStringLiteral() ast.Expression {
	lit := &ast.StringLiteral{Token: p.curToken}
	lit.Value = p.curToken.Literal

	return lit
}

func (p *Parser) parseBooleanLiteral() ast.Expression {
	lit := &ast.BooleanExpression{Token: p.curToken}

	value, err := strconv.ParseBool(lit.TokenLiteral())
	if err != nil {
		msg := fmt.Sprintf("could not parse %q as boolean", lit.TokenLiteral())
		p.errors = append(p.errors, msg)
		return nil
	}

	lit.Value = value

	return lit
}

func (p *Parser) parseNilExpression() ast.Expression {
	return &ast.NilExpression{Token: p.curToken}
}

func (p *Parser) parsePostfixExpression(receiver ast.Expression) ast.Expression {
	arguments := []ast.Expression{}
	return &ast.CallExpression{Token: p.curToken, Receiver: receiver, Method: p.curToken.Literal, Arguments: arguments}
}

func (p *Parser) parseHashExpression() ast.Expression {
	hash := &ast.HashExpression{Token: p.curToken}
	hash.Data = p.parseHashPairs()
	return hash
}

func (p *Parser) parseHashPairs() map[string]ast.Expression {
	pairs := map[string]ast.Expression{}

	if p.peekTokenIs(token.RBrace) {
		p.nextToken() // '}'
		return pairs
	}

	p.parseHashPair(pairs)

	for p.peekTokenIs(token.Comma) {
		p.nextToken()

		p.parseHashPair(pairs)
	}

	if !p.expectPeek(token.RBrace) {
		return nil
	}

	return pairs
}

func (p *Parser) parseHashPair(pairs map[string]ast.Expression) {
	var key string
	var value ast.Expression

	if !p.expectPeek(token.Ident) {
		return
	}

	key = p.parseIdentifier().(*ast.Identifier).Value

	if !p.expectPeek(token.Colon) {
		return
	}

	p.nextToken()
	value = p.parseExpression(LOWEST)
	pairs[key] = value
}

func (p *Parser) parseArrayExpression() ast.Expression {
	arr := &ast.ArrayExpression{Token: p.curToken}
	arr.Elements = p.parseArrayElements()
	return arr
}

func (p *Parser) parseArrayIndexExpression(left ast.Expression) ast.Expression {
	callExpression := &ast.CallExpression{Receiver: left, Method: "[]", Token: p.curToken}

	if p.peekTokenIs(token.RBracket) {
		return nil
	}

	p.nextToken()

	callExpression.Arguments = []ast.Expression{p.parseExpression(LOWEST)}

	if !p.expectPeek(token.RBracket) {
		return nil
	}

	// Assign value to index
	if p.peekTokenIs(token.Assign) {
		p.nextToken()
		p.nextToken()
		assignValue := p.parseExpression(LOWEST)
		callExpression.Method = "[]="
		callExpression.Arguments = append(callExpression.Arguments, assignValue)
	}

	return callExpression
}

func (p *Parser) parseArrayElements() []ast.Expression {
	elems := []ast.Expression{}

	if p.peekTokenIs(token.RBracket) {
		p.nextToken() // ']'
		return elems
	}

	p.nextToken() // start of first expression
	elems = append(elems, p.parseExpression(LOWEST))

	for p.peekTokenIs(token.Comma) {
		p.nextToken() // ","
		p.nextToken() // start of next expression
		elems = append(elems, p.parseExpression(LOWEST))
	}

	if !p.expectPeek(token.RBracket) {
		return nil
	}

	return elems
}

func (p *Parser) parsePrefixExpression() ast.Expression {
	pe := &ast.PrefixExpression{
		Token:    p.curToken,
		Operator: p.curToken.Literal,
	}

	p.nextToken()

	pe.Right = p.parseExpression(PREFIX)

	return pe
}

func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
	exp := &ast.InfixExpression{
		Token:    p.curToken,
		Left:     left,
		Operator: p.curToken.Literal,
	}

	precedence := p.curPrecedence()
	p.nextToken()
	exp.Right = p.parseExpression(precedence)

	return exp
}

func (p *Parser) parseGroupedExpression() ast.Expression {
	p.nextToken()

	exp := p.parseExpression(LOWEST)

	if !p.expectPeek(token.RParen) {
		return nil
	}

	return exp
}

func (p *Parser) parseIfExpression() ast.Expression {
	ie := &ast.IfExpression{Token: p.curToken}
	p.nextToken()
	ie.Condition = p.parseExpression(LOWEST)
	ie.Consequence = p.parseBlockStatement()

	// curToken is now ELSE or RBRACE
	if p.curTokenIs(token.Else) {
		ie.Alternative = p.parseBlockStatement()
	}

	return ie
}

func (p *Parser) parseCallExpression(receiver ast.Expression) ast.Expression {
	var exp *ast.CallExpression

	if p.curTokenIs(token.LParen) || p.curTokenIs(token.Ident) || p.curTokenIs(token.Int) || p.curTokenIs(token.InstanceVariable) {

		m := receiver.(*ast.Identifier).Value
		// receiver is self
		selfTok := token.Token{Type: token.Self, Literal: "self", Line: p.curToken.Line}
		self := &ast.SelfExpression{Token: selfTok}
		receiver = self

		// current token is identifier (method name)
		exp = &ast.CallExpression{Token: p.curToken, Receiver: receiver, Method: m}

		if p.curTokenIs(token.LParen) {
			exp.Arguments = p.parseCallArguments()

		} else {
			exp.Arguments = p.parseCallArgumentsWithoutParens()
		}

	} else if p.curTokenIs(token.Dot) { // call expression has a receiver like: p.foo

		exp = &ast.CallExpression{Token: p.curToken, Receiver: receiver}

		// check if method name is identifier
		if !p.expectPeek(token.Ident) {
			return nil
		}

		exp.Method = p.curToken.Literal

		if p.peekTokenIs(token.LParen) {
			p.nextToken()
			exp.Arguments = p.parseCallArguments()
		} else { // p.foo.bar; || p.foo; || p.foo + 123
			exp.Arguments = []ast.Expression{}
		}
	}

	// Setter method call like: p.foo = x
	if p.peekTokenIs(token.Assign) {
		exp.Method = exp.Method + "="
		p.nextToken()
		p.nextToken()
		exp.Arguments = append(exp.Arguments, p.parseExpression(LOWEST))
	}

	// Parse block
	if p.peekTokenIs(token.Do) && p.acceptBlock {
		p.parseBlockParameters(exp)
	}

	return exp
}

func (p *Parser) parseBlockParameters(exp *ast.CallExpression) {
	p.nextToken()

	// Parse block arguments
	if p.peekTokenIs(token.Bar) {
		var params []*ast.Identifier

		p.nextToken()
		p.nextToken()

		param := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
		params = append(params, param)

		for p.peekTokenIs(token.Comma) {
			p.nextToken()
			p.nextToken()
			param := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
			params = append(params, param)
		}

		if !p.expectPeek(token.Bar) {
			return
		}

		exp.BlockArguments = params
	}

	exp.Block = p.parseBlockStatement()
}

func (p *Parser) parseCallArguments() []ast.Expression {
	args := []ast.Expression{}

	if p.peekTokenIs(token.RParen) {
		p.nextToken() // ')'
		return args
	}

	p.nextToken() // start of first expression
	args = append(args, p.parseExpression(LOWEST))

	for p.peekTokenIs(token.Comma) {
		p.nextToken() // ","
		p.nextToken() // start of next expression
		args = append(args, p.parseExpression(LOWEST))
	}

	if !p.expectPeek(token.RParen) {
		return nil
	}

	return args
}

func (p *Parser) parseCallArgumentsWithoutParens() []ast.Expression {
	args := []ast.Expression{}

	if p.peekTokenIs(token.RParen) {
		p.nextToken() // ')'
		return args
	}

	args = append(args, p.parseExpression(LOWEST))

	for p.peekTokenIs(token.Comma) {
		p.nextToken() // ","
		p.nextToken() // start of next expression
		args = append(args, p.parseExpression(LOWEST))
	}

	if p.peekTokenAtSameLine() {
		return nil
	}
	return args
}

func (p *Parser) parseYieldExpression() ast.Expression {
	ye := &ast.YieldExpression{Token: p.curToken}

	if p.peekTokenIs(token.LParen) {
		p.nextToken()
		ye.Arguments = p.parseCallArguments()
	}

	return ye
}
<commit_msg>add argument hash<commit_after>package parser

import (
	"fmt"
	"strconv"

	"github.com/goby-lang/goby/ast"
	"github.com/goby-lang/goby/token"
)

var argument = map[token.Type]bool{
	token.Int:              true,
	token.String:           true,
	token.True:             true,
	token.False:            true,
	token.InstanceVariable: true,
	token.Ident:            true,
}

var precedence = map[token.Type]int{
	token.Eq:                 EQUALS,
	token.NotEq:              EQUALS,
	token.LT:                 LESSGREATER,
	token.LTE:                LESSGREATER,
	token.GT:                 LESSGREATER,
	token.GTE:                LESSGREATER,
	token.COMP:               LESSGREATER,
	token.And:                LOGIC,
	token.Or:                 LOGIC,
	token.Plus:               SUM,
	token.Minus:              SUM,
	token.Incr:               SUM,
	token.Decr:               SUM,
	token.Modulo:             SUM,
	token.Assign:             ASSIGN,
	token.Slash:              PRODUCT,
	token.Asterisk:           PRODUCT,
	token.Pow:                PRODUCT,
	token.LBracket:           INDEX,
	token.Dot:                CALL,
	token.LParen:             CALL,
	token.ResolutionOperator: CALL,
}

// Constants for denoting precedence
const (
	_ int = iota
	LOWEST
	LOGIC
	EQUALS
	LESSGREATER
	ASSIGN
	SUM
	PRODUCT
	PREFIX
	INDEX
	CALL
)

type (
	prefixParseFn func() ast.Expression
	infixParseFn  func(ast.Expression) ast.Expression
)

func (p *Parser) parseExpression(precedence int) ast.Expression {
	prefix := p.prefixParseFns[p.curToken.Type]
	if prefix == nil {
		p.noPrefixParseFnError(p.curToken.Type)
		return nil
	}

	leftExp := prefix()

	for !p.peekTokenIs(token.Semicolon) && precedence < p.peekPrecedence() && p.peekTokenAtSameLine() {

		infix := p.infixParseFns[p.peekToken.Type]

		if  argument[p.peekToken.Type] && precedence == 0 { // method call without paren case
			infix = p.parseCallExpression
		}

		if infix == nil {
			return leftExp
		}
		p.nextToken()
		leftExp = infix(leftExp)
	}

	return leftExp
}

func (p *Parser) parseSelfExpression() ast.Expression {
	return &ast.SelfExpression{Token: p.curToken}
}

func (p *Parser) parseIdentifier() ast.Expression {
	return &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
}

func (p *Parser) parseConstant() ast.Expression {
	c := &ast.Constant{Token: p.curToken, Value: p.curToken.Literal}

	if p.peekTokenIs(token.ResolutionOperator) {
		p.nextToken()
		return p.parseInfixExpression(c)
	}

	return c
}

func (p *Parser) parseInstanceVariable() ast.Expression {
	return &ast.InstanceVariable{Token: p.curToken, Value: p.curToken.Literal}
}

func (p *Parser) parseIntegerLiteral() ast.Expression {
	lit := &ast.IntegerLiteral{Token: p.curToken}

	value, err := strconv.ParseInt(lit.TokenLiteral(), 0, 64)
	if err != nil {
		msg := fmt.Sprintf("could not parse %q as integer", lit.TokenLiteral())
		p.errors = append(p.errors, msg)
		return nil
	}

	lit.Value = int(value)

	return lit
}

func (p *Parser) parseStringLiteral() ast.Expression {
	lit := &ast.StringLiteral{Token: p.curToken}
	lit.Value = p.curToken.Literal

	return lit
}

func (p *Parser) parseBooleanLiteral() ast.Expression {
	lit := &ast.BooleanExpression{Token: p.curToken}

	value, err := strconv.ParseBool(lit.TokenLiteral())
	if err != nil {
		msg := fmt.Sprintf("could not parse %q as boolean", lit.TokenLiteral())
		p.errors = append(p.errors, msg)
		return nil
	}

	lit.Value = value

	return lit
}

func (p *Parser) parseNilExpression() ast.Expression {
	return &ast.NilExpression{Token: p.curToken}
}

func (p *Parser) parsePostfixExpression(receiver ast.Expression) ast.Expression {
	arguments := []ast.Expression{}
	return &ast.CallExpression{Token: p.curToken, Receiver: receiver, Method: p.curToken.Literal, Arguments: arguments}
}

func (p *Parser) parseHashExpression() ast.Expression {
	hash := &ast.HashExpression{Token: p.curToken}
	hash.Data = p.parseHashPairs()
	return hash
}

func (p *Parser) parseHashPairs() map[string]ast.Expression {
	pairs := map[string]ast.Expression{}

	if p.peekTokenIs(token.RBrace) {
		p.nextToken() // '}'
		return pairs
	}

	p.parseHashPair(pairs)

	for p.peekTokenIs(token.Comma) {
		p.nextToken()

		p.parseHashPair(pairs)
	}

	if !p.expectPeek(token.RBrace) {
		return nil
	}

	return pairs
}

func (p *Parser) parseHashPair(pairs map[string]ast.Expression) {
	var key string
	var value ast.Expression

	if !p.expectPeek(token.Ident) {
		return
	}

	key = p.parseIdentifier().(*ast.Identifier).Value

	if !p.expectPeek(token.Colon) {
		return
	}

	p.nextToken()
	value = p.parseExpression(LOWEST)
	pairs[key] = value
}

func (p *Parser) parseArrayExpression() ast.Expression {
	arr := &ast.ArrayExpression{Token: p.curToken}
	arr.Elements = p.parseArrayElements()
	return arr
}

func (p *Parser) parseArrayIndexExpression(left ast.Expression) ast.Expression {
	callExpression := &ast.CallExpression{Receiver: left, Method: "[]", Token: p.curToken}

	if p.peekTokenIs(token.RBracket) {
		return nil
	}

	p.nextToken()

	callExpression.Arguments = []ast.Expression{p.parseExpression(LOWEST)}

	if !p.expectPeek(token.RBracket) {
		return nil
	}

	// Assign value to index
	if p.peekTokenIs(token.Assign) {
		p.nextToken()
		p.nextToken()
		assignValue := p.parseExpression(LOWEST)
		callExpression.Method = "[]="
		callExpression.Arguments = append(callExpression.Arguments, assignValue)
	}

	return callExpression
}

func (p *Parser) parseArrayElements() []ast.Expression {
	elems := []ast.Expression{}

	if p.peekTokenIs(token.RBracket) {
		p.nextToken() // ']'
		return elems
	}

	p.nextToken() // start of first expression
	elems = append(elems, p.parseExpression(LOWEST))

	for p.peekTokenIs(token.Comma) {
		p.nextToken() // ","
		p.nextToken() // start of next expression
		elems = append(elems, p.parseExpression(LOWEST))
	}

	if !p.expectPeek(token.RBracket) {
		return nil
	}

	return elems
}

func (p *Parser) parsePrefixExpression() ast.Expression {
	pe := &ast.PrefixExpression{
		Token:    p.curToken,
		Operator: p.curToken.Literal,
	}

	p.nextToken()

	pe.Right = p.parseExpression(PREFIX)

	return pe
}

func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
	exp := &ast.InfixExpression{
		Token:    p.curToken,
		Left:     left,
		Operator: p.curToken.Literal,
	}

	precedence := p.curPrecedence()
	p.nextToken()
	exp.Right = p.parseExpression(precedence)

	return exp
}

func (p *Parser) parseGroupedExpression() ast.Expression {
	p.nextToken()

	exp := p.parseExpression(LOWEST)

	if !p.expectPeek(token.RParen) {
		return nil
	}

	return exp
}

func (p *Parser) parseIfExpression() ast.Expression {
	ie := &ast.IfExpression{Token: p.curToken}
	p.nextToken()
	ie.Condition = p.parseExpression(LOWEST)
	ie.Consequence = p.parseBlockStatement()

	// curToken is now ELSE or RBRACE
	if p.curTokenIs(token.Else) {
		ie.Alternative = p.parseBlockStatement()
	}

	return ie
}

func (p *Parser) parseCallExpression(receiver ast.Expression) ast.Expression {
	var exp *ast.CallExpression

	if p.curTokenIs(token.LParen) || p.curTokenIs(token.Ident) || p.curTokenIs(token.Int) || p.curTokenIs(token.InstanceVariable) {

		m := receiver.(*ast.Identifier).Value
		// receiver is self
		selfTok := token.Token{Type: token.Self, Literal: "self", Line: p.curToken.Line}
		self := &ast.SelfExpression{Token: selfTok}
		receiver = self

		// current token is identifier (method name)
		exp = &ast.CallExpression{Token: p.curToken, Receiver: receiver, Method: m}

		if p.curTokenIs(token.LParen) {
			exp.Arguments = p.parseCallArguments()

		} else {
			exp.Arguments = p.parseCallArgumentsWithoutParens()
		}

	} else if p.curTokenIs(token.Dot) { // call expression has a receiver like: p.foo

		exp = &ast.CallExpression{Token: p.curToken, Receiver: receiver}

		// check if method name is identifier
		if !p.expectPeek(token.Ident) {
			return nil
		}

		exp.Method = p.curToken.Literal

		if p.peekTokenIs(token.LParen) {
			p.nextToken()
			exp.Arguments = p.parseCallArguments()
		} else { // p.foo.bar; || p.foo; || p.foo + 123
			exp.Arguments = []ast.Expression{}
		}
	}

	// Setter method call like: p.foo = x
	if p.peekTokenIs(token.Assign) {
		exp.Method = exp.Method + "="
		p.nextToken()
		p.nextToken()
		exp.Arguments = append(exp.Arguments, p.parseExpression(LOWEST))
	}

	// Parse block
	if p.peekTokenIs(token.Do) && p.acceptBlock {
		p.parseBlockParameters(exp)
	}

	return exp
}

func (p *Parser) parseBlockParameters(exp *ast.CallExpression) {
	p.nextToken()

	// Parse block arguments
	if p.peekTokenIs(token.Bar) {
		var params []*ast.Identifier

		p.nextToken()
		p.nextToken()

		param := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
		params = append(params, param)

		for p.peekTokenIs(token.Comma) {
			p.nextToken()
			p.nextToken()
			param := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
			params = append(params, param)
		}

		if !p.expectPeek(token.Bar) {
			return
		}

		exp.BlockArguments = params
	}

	exp.Block = p.parseBlockStatement()
}

func (p *Parser) parseCallArguments() []ast.Expression {
	args := []ast.Expression{}

	if p.peekTokenIs(token.RParen) {
		p.nextToken() // ')'
		return args
	}

	p.nextToken() // start of first expression
	args = append(args, p.parseExpression(LOWEST))

	for p.peekTokenIs(token.Comma) {
		p.nextToken() // ","
		p.nextToken() // start of next expression
		args = append(args, p.parseExpression(LOWEST))
	}

	if !p.expectPeek(token.RParen) {
		return nil
	}

	return args
}

func (p *Parser) parseCallArgumentsWithoutParens() []ast.Expression {
	args := []ast.Expression{}

	if p.peekTokenIs(token.RParen) {
		p.nextToken() // ')'
		return args
	}

	args = append(args, p.parseExpression(LOWEST))

	for p.peekTokenIs(token.Comma) {
		p.nextToken() // ","
		p.nextToken() // start of next expression
		args = append(args, p.parseExpression(LOWEST))
	}

	if p.peekTokenAtSameLine() {
		return nil
	}
	return args
}

func (p *Parser) parseYieldExpression() ast.Expression {
	ye := &ast.YieldExpression{Token: p.curToken}

	if p.peekTokenIs(token.LParen) {
		p.nextToken()
		ye.Arguments = p.parseCallArguments()
	}

	return ye
}
<|endoftext|>


<commit_before>package dnstapio

import (
	"log"
	"net"
	"sync/atomic"
	"time"

	tap "github.com/dnstap/golang-dnstap"
	fs "github.com/farsightsec/golang-framestream"
)

const (
	tcpWriteBufSize = 1024 * 1024
	tcpTimeout      = 4 * time.Second
	flushTimeout    = 1 * time.Second
	queueSize       = 10000
)

type dnstapIO struct {
	endpoint string
	socket   bool
	conn     net.Conn
	enc      *dnstapEncoder
	queue    chan tap.Dnstap
	dropped  uint32
}

// New returns a new and initialized DnstapIO.
func New(endpoint string, socket bool) DnstapIO {
	return &dnstapIO{
		endpoint: endpoint,
		socket:   socket,
		enc: newDnstapEncoder(&fs.EncoderOptions{
			ContentType:   []byte("protobuf:dnstap.Dnstap"),
			Bidirectional: true,
		}),
		queue: make(chan tap.Dnstap, queueSize),
	}
}

// DnstapIO interface
type DnstapIO interface {
	Connect()
	Dnstap(payload tap.Dnstap)
	Close()
}

func (dio *dnstapIO) newConnect() error {
	var err error
	if dio.socket {
		if dio.conn, err = net.Dial("unix", dio.endpoint); err != nil {
			return err
		}
	} else {
		if dio.conn, err = net.DialTimeout("tcp", dio.endpoint, tcpTimeout); err != nil {
			return err
		}
		if tcpConn, ok := dio.conn.(*net.TCPConn); ok {
			tcpConn.SetWriteBuffer(tcpWriteBufSize)
			tcpConn.SetNoDelay(false)
		}
	}

	if err = dio.enc.resetWriter(dio.conn); err != nil {
		return err
	}
	return nil
}

// Connect connects to the dnstop endpoint.
func (dio *dnstapIO) Connect() {
	if err := dio.newConnect(); err != nil {
		log.Printf("[ERROR] No connection to dnstap endpoint")
	}
	go dio.serve()
}

// Dnstap enqueues the payload for log.
func (dio *dnstapIO) Dnstap(payload tap.Dnstap) {
	select {
	case dio.queue <- payload:
	default:
		atomic.AddUint32(&dio.dropped, 1)
	}
}

func (dio *dnstapIO) closeConnection() {
	dio.enc.close()
	if dio.conn != nil {
		dio.conn.Close()
		dio.conn = nil
	}
}

// Close waits until the I/O routine is finished to return.
func (dio *dnstapIO) Close() {
	close(dio.queue)
}

func (dio *dnstapIO) flushBuffer() {
	if dio.conn == nil {
		if err := dio.newConnect(); err != nil {
			return
		}
		log.Printf("[INFO] Reconnected to dnstap")
	}

	if err := dio.enc.flushBuffer(); err != nil {
		log.Printf("[WARN] Connection lost: %s", err)
		dio.closeConnection()
		if err := dio.newConnect(); err != nil {
			log.Printf("[ERROR] Cannot connect to dnstap: %s", err)
		} else {
			log.Printf("[INFO] Reconnected to dnstap")
		}
	}
}

func (dio *dnstapIO) write(payload *tap.Dnstap) {
	if err := dio.enc.writeMsg(payload); err != nil {
		atomic.AddUint32(&dio.dropped, 1)
	}
}

func (dio *dnstapIO) serve() {
	timeout := time.After(flushTimeout)
	for {
		select {
		case payload, ok := <-dio.queue:
			if !ok {
				dio.flushBuffer()
				dio.closeConnection()
				return
			}
			dio.write(&payload)
		case <-timeout:
			if dropped := atomic.SwapUint32(&dio.dropped, 0); dropped > 0 {
				log.Printf("[WARN] Dropped dnstap messages: %d", dropped)
			}
			dio.flushBuffer()
			timeout = time.After(flushTimeout)
		}
	}
}
<commit_msg>Golint fix (#1475)<commit_after>package dnstapio

import (
	"log"
	"net"
	"sync/atomic"
	"time"

	tap "github.com/dnstap/golang-dnstap"
	fs "github.com/farsightsec/golang-framestream"
)

const (
	tcpWriteBufSize = 1024 * 1024
	tcpTimeout      = 4 * time.Second
	flushTimeout    = 1 * time.Second
	queueSize       = 10000
)

type dnstapIO struct {
	endpoint string
	socket   bool
	conn     net.Conn
	enc      *dnstapEncoder
	queue    chan tap.Dnstap
	dropped  uint32
}

// New returns a new and initialized DnstapIO.
func New(endpoint string, socket bool) DnstapIO {
	return &dnstapIO{
		endpoint: endpoint,
		socket:   socket,
		enc: newDnstapEncoder(&fs.EncoderOptions{
			ContentType:   []byte("protobuf:dnstap.Dnstap"),
			Bidirectional: true,
		}),
		queue: make(chan tap.Dnstap, queueSize),
	}
}

// DnstapIO interface
type DnstapIO interface {
	Connect()
	Dnstap(payload tap.Dnstap)
	Close()
}

func (dio *dnstapIO) newConnect() error {
	var err error
	if dio.socket {
		if dio.conn, err = net.Dial("unix", dio.endpoint); err != nil {
			return err
		}
	} else {
		if dio.conn, err = net.DialTimeout("tcp", dio.endpoint, tcpTimeout); err != nil {
			return err
		}
		if tcpConn, ok := dio.conn.(*net.TCPConn); ok {
			tcpConn.SetWriteBuffer(tcpWriteBufSize)
			tcpConn.SetNoDelay(false)
		}
	}

	return dio.enc.resetWriter(dio.conn)
}

// Connect connects to the dnstop endpoint.
func (dio *dnstapIO) Connect() {
	if err := dio.newConnect(); err != nil {
		log.Printf("[ERROR] No connection to dnstap endpoint")
	}
	go dio.serve()
}

// Dnstap enqueues the payload for log.
func (dio *dnstapIO) Dnstap(payload tap.Dnstap) {
	select {
	case dio.queue <- payload:
	default:
		atomic.AddUint32(&dio.dropped, 1)
	}
}

func (dio *dnstapIO) closeConnection() {
	dio.enc.close()
	if dio.conn != nil {
		dio.conn.Close()
		dio.conn = nil
	}
}

// Close waits until the I/O routine is finished to return.
func (dio *dnstapIO) Close() {
	close(dio.queue)
}

func (dio *dnstapIO) flushBuffer() {
	if dio.conn == nil {
		if err := dio.newConnect(); err != nil {
			return
		}
		log.Printf("[INFO] Reconnected to dnstap")
	}

	if err := dio.enc.flushBuffer(); err != nil {
		log.Printf("[WARN] Connection lost: %s", err)
		dio.closeConnection()
		if err := dio.newConnect(); err != nil {
			log.Printf("[ERROR] Cannot connect to dnstap: %s", err)
		} else {
			log.Printf("[INFO] Reconnected to dnstap")
		}
	}
}

func (dio *dnstapIO) write(payload *tap.Dnstap) {
	if err := dio.enc.writeMsg(payload); err != nil {
		atomic.AddUint32(&dio.dropped, 1)
	}
}

func (dio *dnstapIO) serve() {
	timeout := time.After(flushTimeout)
	for {
		select {
		case payload, ok := <-dio.queue:
			if !ok {
				dio.flushBuffer()
				dio.closeConnection()
				return
			}
			dio.write(&payload)
		case <-timeout:
			if dropped := atomic.SwapUint32(&dio.dropped, 0); dropped > 0 {
				log.Printf("[WARN] Dropped dnstap messages: %d", dropped)
			}
			dio.flushBuffer()
			timeout = time.After(flushTimeout)
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>fix(api): checks binaries availability on startup (#4083)<commit_after><|endoftext|>


<commit_before><commit_msg>Refactor tests and add test for Text() function<commit_after><|endoftext|>


<commit_before>package filestore

import (
	"fmt"
	"io"
	"os"

	ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore"
	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query"
	b58 "gx/ipfs/QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf/go-base58"
)

const (
	StatusOk      = 1
	StatusMissing = 2
	StatusInvalid = 3
	StatusError   = 4
)

func statusStr(status int) string {
	switch status {
	case 0:
		return ""
	case 1:
		return "ok      "
	case 2:
		return "missing "
	case 3:
		return "invalid "
	case 4:
		return "error   "
	default:
		return "??      "
	}
}

type ListRes struct {
	Key []byte
	DataObj
	Status int
}

func (r *ListRes) Format() string {
	mhash := b58.Encode(r.Key)
	return fmt.Sprintf("%s%s %s\n", statusStr(r.Status), mhash, r.DataObj.Format())
}

func list(d *Datastore, out chan<- *ListRes, verify bool) error {
	qr, err := d.Query(query.Query{KeysOnly: true})
	if err != nil {
		return err
	}
	for r := range qr.Next() {
		if r.Error != nil {
			return r.Error
		}
		key := ds.NewKey(r.Key)
		val, _ := d.GetDirect(key)
		status := 0
		if verify {
			_, err := d.GetData(key, val, true)
			if err == nil {
				status = StatusOk
			} else if os.IsNotExist(err) {
				status = StatusMissing
			} else if _, ok := err.(InvalidBlock); ok || err == io.EOF || err == io.ErrUnexpectedEOF {
				status = StatusInvalid
			} else {
				status = StatusError
			}
		}
		out <- &ListRes{key.Bytes()[1:], val.StripData(), status}
	}
	return nil
}

func List(d *Datastore, out chan<- *ListRes) error { return list(d, out, false) }

func Verify(d *Datastore, out chan<- *ListRes) error { return list(d, out, true) }
<commit_msg>"filestore verify": change "invalid" status to "changed".<commit_after>package filestore

import (
	"fmt"
	"io"
	"os"

	ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore"
	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query"
	b58 "gx/ipfs/QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf/go-base58"
)

const (
	StatusOk      = 1
	StatusChanged = 2
	StatusMissing = 3
	StatusError   = 4
)

func statusStr(status int) string {
	switch status {
	case 0:
		return ""
	case StatusOk:
		return "ok       "
	case StatusChanged:
		return "changed  "
	case StatusMissing:
		return "missing  "
	case StatusError:
		return "error    "
	default:
		return "??       "
	}
}

type ListRes struct {
	Key []byte
	DataObj
	Status int
}

func (r *ListRes) Format() string {
	mhash := b58.Encode(r.Key)
	return fmt.Sprintf("%s%s %s\n", statusStr(r.Status), mhash, r.DataObj.Format())
}

func list(d *Datastore, out chan<- *ListRes, verify bool) error {
	qr, err := d.Query(query.Query{KeysOnly: true})
	if err != nil {
		return err
	}
	for r := range qr.Next() {
		if r.Error != nil {
			return r.Error
		}
		key := ds.NewKey(r.Key)
		val, _ := d.GetDirect(key)
		status := 0
		if verify {
			_, err := d.GetData(key, val, true)
			if err == nil {
				status = StatusOk
			} else if os.IsNotExist(err) {
				status = StatusMissing
			} else if _, ok := err.(InvalidBlock); ok || err == io.EOF || err == io.ErrUnexpectedEOF {
				status = StatusChanged
			} else {
				status = StatusError
			}
		}
		out <- &ListRes{key.Bytes()[1:], val.StripData(), status}
	}
	return nil
}

func List(d *Datastore, out chan<- *ListRes) error { return list(d, out, false) }

func Verify(d *Datastore, out chan<- *ListRes) error { return list(d, out, true) }
<|endoftext|>


<commit_before>package plugin

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"
	"runtime"
	"strings"
)

type installTarget struct {
	owner      string
	repo       string
	pluginName string
	releaseTag string

	// fields for testing
	rawGithubURL string
	apiGithubURL string
}

const (
	defaultRawGithubURL = "https://raw.githubusercontent.com"
	defaultAPIGithubURL = "https://api.github.com"
)

// the pattern of installTarget string
// (?:<plugin_name>|<owner>/<repo>)(?:@<releaseTag>)?
var targetReg = regexp.MustCompile(`^(?:([^@/]+)/([^@/]+)|([^@/]+))(?:@(.+))?$`)

// Parse install target string, and construct installTarget
// example is below
// - mackerelio/mackerel-plugin-sample
// - mackerel-plugin-sample
// - mackerelio/mackerel-plugin-sample@v0.0.1
func newInstallTargetFromString(target string) (*installTarget, error) {
	matches := targetReg.FindStringSubmatch(target)
	if len(matches) != 5 {
		return nil, fmt.Errorf("Install target is invalid: %s", target)
	}

	it := &installTarget{
		owner:      matches[1],
		repo:       matches[2],
		pluginName: matches[3],
		releaseTag: matches[4],
	}
	return it, nil
}

// Make artifact's download URL
func (it *installTarget) makeDownloadURL() (string, error) {
	owner, repo, err := it.getOwnerAndRepo()
	if err != nil {
		return "", err
	}

	releaseTag, err := it.getReleaseTag(owner, repo)
	if err != nil {
		return "", err
	}

	filename := fmt.Sprintf("%s_%s_%s.zip", url.PathEscape(repo), runtime.GOOS, runtime.GOARCH)
	downloadURL := fmt.Sprintf(
		"https://github.com/%s/%s/releases/download/%s/%s",
		url.PathEscape(owner),
		url.PathEscape(repo),
		url.PathEscape(releaseTag),
		filename,
	)

	return downloadURL, nil
}

func (it *installTarget) getOwnerAndRepo() (string, string, error) {
	if it.owner != "" && it.repo != "" {
		return it.owner, it.repo, nil
	}

	// Get owner and repo from plugin registry
	defURL := fmt.Sprintf(
		"%s/mackerelio/plugin-registry/master/plugins/%s.json",
		it.getRawGithubURL(),
		url.PathEscape(it.pluginName),
	)
	resp, err := (&client{}).get(defURL)
	if err != nil {
		return "", "", err
	}
	defer resp.Body.Close()

	var def registryDef
	err = json.NewDecoder(resp.Body).Decode(&def)
	if err != nil {
		return "", "", err
	}

	ownerAndRepo := strings.Split(def.Source, "/")
	if len(ownerAndRepo) != 2 {
		return "", "", fmt.Errorf("source definition is invalid")
	}

	// Cache owner and repo
	it.owner = ownerAndRepo[0]
	it.repo = ownerAndRepo[1]

	return it.owner, it.repo, nil
}

func (it *installTarget) getReleaseTag(owner, repo string) (string, error) {
	if it.releaseTag != "" {
		return it.releaseTag, nil
	}

	// Get latest release tag from Github API
	ctx := context.Background()
	client := getGithubClient(ctx)
	client.BaseURL = it.getAPIGithubURL()

	release, _, err := client.Repositories.GetLatestRelease(ctx, owner, repo)
	if err != nil {
		return "", err
	}

	// Cache releaseTag
	it.releaseTag = release.GetTagName()
	return it.releaseTag, nil
}

func (it *installTarget) getRawGithubURL() string {
	if it.rawGithubURL != "" {
		return it.rawGithubURL
	}
	return defaultRawGithubURL
}

// Returns URL object which Github Client.BaseURL can receive as it is
func (it *installTarget) getAPIGithubURL() *url.URL {
	u := defaultAPIGithubURL
	if it.apiGithubURL != "" {
		u = it.apiGithubURL
	}
	// Ignore err because apiGithubURL is specified only internally
	apiURL, _ := url.Parse(u + "/") // trailing `/` is required for BaseURL
	return apiURL
}

// registryDef represents one plugin definition in plugin-registry
// See Also: https://github.com/mackerelio/plugin-registry
type registryDef struct {
	Source      string `json:"source"`
	Description string `json:"description"`
}
<commit_msg>[plugin.install] support direct URL target<commit_after>package plugin

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"
	"runtime"
	"strings"
)

type installTarget struct {
	owner      string
	repo       string
	pluginName string
	releaseTag string
	directURL  string

	// fields for testing
	rawGithubURL string
	apiGithubURL string
}

const (
	defaultRawGithubURL = "https://raw.githubusercontent.com"
	defaultAPIGithubURL = "https://api.github.com"
)

// the pattern of installTarget string
// (?:<plugin_name>|<owner>/<repo>)(?:@<releaseTag>)?
var targetReg = regexp.MustCompile(`^(?:([^@/]+)/([^@/]+)|([^@/]+))(?:@(.+))?$`)

// Parse install target string, and construct installTarget
// example is below
// - mackerelio/mackerel-plugin-sample
// - mackerel-plugin-sample
// - mackerelio/mackerel-plugin-sample@v0.0.1
func newInstallTargetFromString(target string) (*installTarget, error) {
	if strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://") {
		return &installTarget{
			directURL: target,
		}, nil
	}

	matches := targetReg.FindStringSubmatch(target)
	if len(matches) != 5 {
		return nil, fmt.Errorf("Install target is invalid: %s", target)
	}

	it := &installTarget{
		owner:      matches[1],
		repo:       matches[2],
		pluginName: matches[3],
		releaseTag: matches[4],
	}
	return it, nil
}

// Make artifact's download URL
func (it *installTarget) makeDownloadURL() (string, error) {
	if it.directURL != "" {
		return it.directURL, nil
	}

	owner, repo, err := it.getOwnerAndRepo()
	if err != nil {
		return "", err
	}

	releaseTag, err := it.getReleaseTag(owner, repo)
	if err != nil {
		return "", err
	}

	filename := fmt.Sprintf("%s_%s_%s.zip", url.PathEscape(repo), runtime.GOOS, runtime.GOARCH)
	downloadURL := fmt.Sprintf(
		"https://github.com/%s/%s/releases/download/%s/%s",
		url.PathEscape(owner),
		url.PathEscape(repo),
		url.PathEscape(releaseTag),
		filename,
	)

	return downloadURL, nil
}

func (it *installTarget) getOwnerAndRepo() (string, string, error) {
	if it.owner != "" && it.repo != "" {
		return it.owner, it.repo, nil
	}

	// Get owner and repo from plugin registry
	defURL := fmt.Sprintf(
		"%s/mackerelio/plugin-registry/master/plugins/%s.json",
		it.getRawGithubURL(),
		url.PathEscape(it.pluginName),
	)
	resp, err := (&client{}).get(defURL)
	if err != nil {
		return "", "", err
	}
	defer resp.Body.Close()

	var def registryDef
	err = json.NewDecoder(resp.Body).Decode(&def)
	if err != nil {
		return "", "", err
	}

	ownerAndRepo := strings.Split(def.Source, "/")
	if len(ownerAndRepo) != 2 {
		return "", "", fmt.Errorf("source definition is invalid")
	}

	// Cache owner and repo
	it.owner = ownerAndRepo[0]
	it.repo = ownerAndRepo[1]

	return it.owner, it.repo, nil
}

func (it *installTarget) getReleaseTag(owner, repo string) (string, error) {
	if it.releaseTag != "" {
		return it.releaseTag, nil
	}

	// Get latest release tag from Github API
	ctx := context.Background()
	client := getGithubClient(ctx)
	client.BaseURL = it.getAPIGithubURL()

	release, _, err := client.Repositories.GetLatestRelease(ctx, owner, repo)
	if err != nil {
		return "", err
	}

	// Cache releaseTag
	it.releaseTag = release.GetTagName()
	return it.releaseTag, nil
}

func (it *installTarget) getRawGithubURL() string {
	if it.rawGithubURL != "" {
		return it.rawGithubURL
	}
	return defaultRawGithubURL
}

// Returns URL object which Github Client.BaseURL can receive as it is
func (it *installTarget) getAPIGithubURL() *url.URL {
	u := defaultAPIGithubURL
	if it.apiGithubURL != "" {
		u = it.apiGithubURL
	}
	// Ignore err because apiGithubURL is specified only internally
	apiURL, _ := url.Parse(u + "/") // trailing `/` is required for BaseURL
	return apiURL
}

// registryDef represents one plugin definition in plugin-registry
// See Also: https://github.com/mackerelio/plugin-registry
type registryDef struct {
	Source      string `json:"source"`
	Description string `json:"description"`
}
<|endoftext|>


<commit_before>// Copyright © 2017 Nelz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package xbo

import (
	"testing"
	"time"
)

func TestConvenienceBackOffs(t *testing.T) {
	// Testing each of the convenience BackOff types
	testCases := []struct {
		bo  BackOff
		dur time.Duration
		err error
	}{
		{
			NewConstant(time.Minute),
			time.Minute,
			nil,
		},
		{
			NewZero(),
			ZeroDuration,
			nil,
		},
		{
			NewStop(),
			ZeroDuration,
			ErrStop,
		},
	}

	// We want to test that each of the convenience BackOff types
	// produce consistent output.
	iters := []int{11, 3, 7}
	for _, tc := range testCases {
		for _, iter := range iters {
			for ix := 0; ix < iter; ix++ {
				dur, err := tc.bo.Next(false)
				if dur != tc.dur {
					t.Errorf("expected %s: %s", tc.dur, dur)
				}
				if err != tc.err {
					t.Errorf("expected %v: %v", tc.err, err)
				}
			}

			// Also test that reset gets the expected standard results
			dur, err := tc.bo.Next(true)
			if dur != ZeroDuration {
				t.Errorf("expected %s: %s", ZeroDuration, dur)
			}
			if err != nil {
				t.Errorf("unexpected: %v", err)
			}
		}
	}
}
<commit_msg>Ability to define concrete durations<commit_after>// Copyright © 2017 Nelz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package xbo

import (
	"testing"
	"time"
)

func TestConvenienceBackOffs(t *testing.T) {
	// Testing each of the convenience BackOff types
	testCases := []struct {
		bo  BackOff
		dur time.Duration
		err error
	}{
		{
			NewConstant(time.Minute),
			time.Minute,
			nil,
		},
		{
			NewZero(),
			ZeroDuration,
			nil,
		},
		{
			NewStop(),
			ZeroDuration,
			ErrStop,
		},
		{
			NewLoop([]time.Duration{}, true),
			ZeroDuration,
			ErrStop,
		},
		{
			NewLimit([]time.Duration{}, true),
			ZeroDuration,
			ErrStop,
		},
		{
			NewEcho([]time.Duration{}, true),
			ZeroDuration,
			ErrStop,
		},
	}

	// We want to test that each of the convenience BackOff types
	// produce consistent output.
	iters := []int{11, 3, 7}
	for _, tc := range testCases {
		for _, iter := range iters {
			for ix := 0; ix < iter; ix++ {
				dur, err := tc.bo.Next(false)
				if dur != tc.dur {
					t.Errorf("expected %s: %s", tc.dur, dur)
				}
				if err != tc.err {
					t.Errorf("expected %v: %v", tc.err, err)
				}
			}

			// Also test that reset gets the expected standard results
			dur, err := tc.bo.Next(true)
			if dur != ZeroDuration {
				t.Errorf("expected %s: %s", ZeroDuration, dur)
			}
			if err != nil {
				t.Errorf("unexpected: %v", err)
			}
		}
	}
}
<|endoftext|>


<commit_before>package common

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync"
	"unicode"

	"github.com/ryanuber/columnize"
)

type errfunc func() error

// AppRoot returns the app root path
func AppRoot(appName string) string {
	dokkuRoot := MustGetEnv("DOKKU_ROOT")
	return fmt.Sprintf("%v/%v", dokkuRoot, appName)
}

// AppHostRoot returns the app root path
func AppHostRoot(appName string) string {
	dokkuHostRoot := MustGetEnv("DOKKU_HOST_ROOT")
	return fmt.Sprintf("%v/%v", dokkuHostRoot, appName)
}

// AskForDestructiveConfirmation checks for confirmation on destructive actions
func AskForDestructiveConfirmation(name string, objectType string) error {
	LogWarn("WARNING: Potentially Destructive Action")
	LogWarn(fmt.Sprintf("This command will destroy %v %v.", objectType, name))
	LogWarn(fmt.Sprintf("To proceed, type \"%v\"", name))
	fmt.Print("> ")
	var response string
	_, err := fmt.Scanln(&response)
	if err != nil {
		return err
	}

	if response != name {
		LogStderr("Confirmation did not match test. Aborted.")
		os.Exit(1)
		return nil
	}

	return nil
}

// CommandUsage outputs help for a command
func CommandUsage(helpHeader string, helpContent string) {
	config := columnize.DefaultConfig()
	config.Delim = ","
	config.Prefix = "    "
	config.Empty = ""
	content := strings.Split(helpContent, "\n")[1:]
	fmt.Println(helpHeader)
	fmt.Println(columnize.Format(content, config))
}

// GetAppScheduler fetches the scheduler for a given application
func GetAppScheduler(appName string) string {
	appScheduler := ""
	globalScheduler := ""

	var wg sync.WaitGroup

	if appName != "--global" {
		wg.Add(1)
		go func() {
			defer wg.Done()
			b, _ := PlugnTriggerOutput("config-get", []string{appName, "DOKKU_SCHEDULER"}...)
			value := strings.TrimSpace(string(b[:]))
			if value != "" {
				appScheduler = value
			}
		}()
	}
	wg.Add(1)
	go func() {
		defer wg.Done()
		globalScheduler = GetGlobalScheduler()
	}()

	wg.Wait()

	if appScheduler == "" {
		appScheduler = globalScheduler
	}
	return appScheduler
}

// GetGlobalScheduler fetchs the global scheduler
func GetGlobalScheduler() string {
	b, _ := PlugnTriggerOutput("config-get-global", []string{"DOKKU_SCHEDULER"}...)
	value := strings.TrimSpace(string(b[:]))
	if value != "" {
		return value
	}

	return "docker-local"
}

// GetDeployingAppImageName returns deploying image identifier for a given app, tag tuple. validate if tag is presented
func GetDeployingAppImageName(appName, imageTag, imageRepo string) (string, error) {
	b, err := PlugnTriggerOutput("deployed-app-repository", []string{appName}...)
	if err != nil {
		return "", err
	}
	imageRemoteRepository := strings.TrimSpace(string(b[:]))

	b, err = PlugnTriggerOutput("deployed-app-image-tag", []string{appName}...)
	if err != nil {
		return "", err
	}
	newImageTag := strings.TrimSpace(string(b[:]))

	b, err = PlugnTriggerOutput("deployed-app-image-repo", []string{appName}...)
	if err != nil {
		return "", err
	}
	newImageRepo := strings.TrimSpace(string(b[:]))

	if newImageRepo != "" {
		imageRepo = newImageRepo
	}
	if newImageTag != "" {
		imageTag = newImageTag
	}
	if imageRepo == "" {
		imageRepo = GetAppImageRepo(appName)
	}
	if imageTag == "" {
		imageTag = "latest"
	}

	imageName := fmt.Sprintf("%s%s:%s", imageRemoteRepository, imageRepo, imageTag)
	if !VerifyImage(imageName) {
		return "", fmt.Errorf("App image (%s) not found", imageName)
	}
	return imageName, nil
}

// GetAppImageRepo is the central definition of a dokku image repo pattern
func GetAppImageRepo(appName string) string {
	return strings.Join([]string{"dokku", appName}, "/")
}

// GetAppContainerIDs returns a list of docker container ids for given app and optional container_type
func GetAppContainerIDs(appName string, containerType string) ([]string, error) {
	var containerIDs []string
	appRoot := AppRoot(appName)
	containerFilePath := fmt.Sprintf("%v/CONTAINER", appRoot)
	_, err := os.Stat(containerFilePath)
	if !os.IsNotExist(err) {
		containerIDs = append(containerIDs, ReadFirstLine(containerFilePath))
	}

	containerPattern := fmt.Sprintf("%v/CONTAINER.*", appRoot)
	if containerType != "" {
		containerPattern = fmt.Sprintf("%v/CONTAINER.%v.*", appRoot, containerType)
		if strings.Contains(".", containerType) {
			containerPattern = fmt.Sprintf("%v/CONTAINER.%v", appRoot, containerType)
		}
	}

	files, _ := filepath.Glob(containerPattern)
	for _, containerFile := range files {
		containerIDs = append(containerIDs, ReadFirstLine(containerFile))
	}

	return containerIDs, nil
}

// GetAppRunningContainerIDs return a list of running docker container ids for given app and optional container_type
func GetAppRunningContainerIDs(appName string, containerType string) ([]string, error) {
	var runningContainerIDs []string
	if !IsDeployed(appName) {
		LogFail(fmt.Sprintf("App %v has not been deployed", appName))
	}

	containerIDs, err := GetAppContainerIDs(appName, containerType)
	if err != nil {
		return runningContainerIDs, nil
	}
	for _, containerID := range containerIDs {
		if ContainerIsRunning(containerID) {
			runningContainerIDs = append(runningContainerIDs, containerID)
		}
	}

	return runningContainerIDs, nil
}

// GetRunningImageTag retrieves current image tag for a given app and returns empty string if no deployed containers are found
func GetRunningImageTag(appName string) (string, error) {
	containerIDs, err := GetAppContainerIDs(appName, "")
	if err != nil {
		return "", err
	}

	for _, containerID := range containerIDs {
		if image, err := DockerInspect(containerID, "{{ .Config.Image }}"); err == nil {
			return strings.Split(image, ":")[1], nil
		}
	}

	return "", errors.New("No image tag found")
}

// DokkuApps returns a list of all local apps
func DokkuApps() (apps []string, err error) {
	dokkuRoot := MustGetEnv("DOKKU_ROOT")
	files, err := ioutil.ReadDir(dokkuRoot)
	if err != nil {
		err = fmt.Errorf("You haven't deployed any applications yet")
		return
	}

	for _, f := range files {
		appRoot := AppRoot(f.Name())
		if !DirectoryExists(appRoot) {
			continue
		}
		if strings.HasPrefix(f.Name(), ".") {
			continue
		}
		apps = append(apps, f.Name())
	}

	if len(apps) == 0 {
		err = fmt.Errorf("You haven't deployed any applications yet")
		return
	}

	return
}

// GetAppImageName returns image identifier for a given app, tag tuple. validate if tag is presented
func GetAppImageName(appName, imageTag, imageRepo string) (imageName string) {
	if imageRepo == "" {
		imageRepo = GetAppImageRepo(appName)
	}

	if imageTag == "" {
		imageName = fmt.Sprintf("%v:latest", imageRepo)
	} else {
		imageName = fmt.Sprintf("%v:%v", imageRepo, imageTag)
		if !VerifyImage(imageName) {
			LogFail(fmt.Sprintf("App image (%s) not found", imageName))
		}
	}
	return
}

// IsDeployed returns true if given app has a running container
func IsDeployed(appName string) bool {
	scheduler := GetAppScheduler(appName)
	_, err := PlugnTriggerOutput("scheduler-is-deployed", []string{scheduler, appName}...)
	return err == nil
}

// MustGetEnv returns env variable or fails if it's not set
func MustGetEnv(key string) (val string) {
	val = os.Getenv(key)
	if val == "" {
		LogFail(fmt.Sprintf("%s not set!", key))
	}
	return
}

// GetenvWithDefault returns env variable or defaultValue if it's not set
func GetenvWithDefault(key string, defaultValue string) (val string) {
	val = os.Getenv(key)
	if val == "" {
		val = defaultValue
	}
	return
}

// ParseReportArgs splits out flags from non-flags for input into report commands
func ParseReportArgs(pluginName string, arguments []string) ([]string, string, error) {
	osArgs := []string{}
	infoFlags := []string{}
	for _, argument := range arguments {
		if strings.HasPrefix(argument, "--") {
			infoFlags = append(infoFlags, argument)
		} else {
			osArgs = append(osArgs, argument)
		}
	}

	if len(infoFlags) == 0 {
		return osArgs, "", nil
	}
	if len(infoFlags) == 1 {
		return osArgs, infoFlags[0], nil
	}
	return osArgs, "", fmt.Errorf("%s:report command allows only a single flag", pluginName)
}

// ReportSingleApp is an internal function that displays a report for an app
func ReportSingleApp(reportType string, appName string, infoFlag string, infoFlags map[string]string, trimPrefix bool, uppercaseFirstCharacter bool) error {
	flags := []string{}
	for key := range infoFlags {
		flags = append(flags, key)
	}
	sort.Strings(flags)

	if len(infoFlag) == 0 {
		LogInfo2Quiet(fmt.Sprintf("%s %v information", appName, reportType))
		for _, k := range flags {
			v := infoFlags[k]
			prefix := "--"
			if trimPrefix {
				prefix = fmt.Sprintf("--%v-", reportType)
			}

			key := strings.Replace(strings.Replace(strings.TrimPrefix(k, prefix), "-", " ", -1), ".", " ", -1)

			if uppercaseFirstCharacter {
				key = UcFirst(key)
			}

			LogVerbose(fmt.Sprintf("%s%s", RightPad(fmt.Sprintf("%s:", key), 31, " "), v))
		}
		return nil
	}

	for _, k := range flags {
		if infoFlag == k {
			v := infoFlags[k]
			fmt.Println(v)
			return nil
		}
	}

	keys := reflect.ValueOf(infoFlags).MapKeys()
	strkeys := make([]string, len(keys))
	for i := 0; i < len(keys); i++ {
		strkeys[i] = keys[i].String()
	}

	return fmt.Errorf("Invalid flag passed, valid flags: %s", strings.Join(strkeys, ", "))
}

// RightPad right-pads the string with pad up to len runes
func RightPad(str string, length int, pad string) string {
	return str + times(pad, length-len(str))
}

// ShiftString removes the first and returns that entry as well as the rest of the list
func ShiftString(a []string) (string, []string) {
	if len(a) == 0 {
		return "", a
	}

	return a[0], a[1:]
}

// StripInlineComments removes bash-style comment from input line
func StripInlineComments(text string) string {
	b := []byte(text)
	re := regexp.MustCompile("(?s)#.*")
	b = re.ReplaceAll(b, nil)
	return strings.TrimSpace(string(b))
}

// SuppressOutput suppresses the output of a function unless there is an error
func SuppressOutput(f errfunc) error {
	rescueStdout := os.Stdout
	r, w, _ := os.Pipe()
	os.Stdout = w

	err := f()

	w.Close()
	out, _ := ioutil.ReadAll(r)
	os.Stdout = rescueStdout

	if err != nil {
		fmt.Printf(string(out[:]))
	}

	return err
}

// ToBool returns a bool value for a given string
func ToBool(s string) bool {
	return s == "true"
}

// ToInt returns an int value for a given string
func ToInt(s string, defaultValue int) int {
	i, err := strconv.Atoi(s)
	if err != nil {
		return defaultValue
	}

	return i
}

// UcFirst uppercases the first character in a string
func UcFirst(str string) string {
	for i, v := range str {
		return string(unicode.ToUpper(v)) + str[i+1:]
	}
	return ""
}

// IsValidAppName verifies that the app name matches naming restrictions
func IsValidAppName(appName string) error {
	if appName == "" {
		return errors.New("Please specify an app to run the command on")
	}

	r, _ := regexp.Compile("^[a-z0-9][^/:_A-Z]*$")
	if r.MatchString(appName) {
		return nil
	}

	return errors.New("App name must begin with lowercase alphanumeric character, and cannot include uppercase characters, colons, or underscores")
}

// isValidAppNameOld verifies that the app name matches the old naming restrictions
func isValidAppNameOld(appName string) error {
	if appName == "" {
		return errors.New("Please specify an app to run the command on")
	}

	r, _ := regexp.Compile("^[a-z0-9][^/:A-Z]*$")
	if r.MatchString(appName) {
		return nil
	}

	return errors.New("App name must begin with lowercase alphanumeric character, and cannot include uppercase characters, or colons")
}

// VerifyAppName checks if an app conforming to either the old or new
// naming conventions exists
func VerifyAppName(appName string) error {
	newErr := IsValidAppName(appName)
	oldErr := isValidAppNameOld(appName)
	if newErr != nil && oldErr != nil {
		return newErr
	}

	appRoot := AppRoot(appName)
	if !DirectoryExists(appRoot) {
		return fmt.Errorf("App %s does not exist", appName)
	}

	return nil
}

func times(str string, n int) (out string) {
	for i := 0; i < n; i++ {
		out += str
	}
	return
}
<commit_msg>refactor: simplify code<commit_after>package common

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync"
	"unicode"

	"github.com/ryanuber/columnize"
)

type errfunc func() error

// AppRoot returns the app root path
func AppRoot(appName string) string {
	dokkuRoot := MustGetEnv("DOKKU_ROOT")
	return fmt.Sprintf("%v/%v", dokkuRoot, appName)
}

// AppHostRoot returns the app root path
func AppHostRoot(appName string) string {
	dokkuHostRoot := MustGetEnv("DOKKU_HOST_ROOT")
	return fmt.Sprintf("%v/%v", dokkuHostRoot, appName)
}

// AskForDestructiveConfirmation checks for confirmation on destructive actions
func AskForDestructiveConfirmation(name string, objectType string) error {
	LogWarn("WARNING: Potentially Destructive Action")
	LogWarn(fmt.Sprintf("This command will destroy %v %v.", objectType, name))
	LogWarn(fmt.Sprintf("To proceed, type \"%v\"", name))
	fmt.Print("> ")
	var response string
	_, err := fmt.Scanln(&response)
	if err != nil {
		return err
	}

	if response != name {
		LogStderr("Confirmation did not match test. Aborted.")
		os.Exit(1)
		return nil
	}

	return nil
}

// CommandUsage outputs help for a command
func CommandUsage(helpHeader string, helpContent string) {
	config := columnize.DefaultConfig()
	config.Delim = ","
	config.Prefix = "    "
	config.Empty = ""
	content := strings.Split(helpContent, "\n")[1:]
	fmt.Println(helpHeader)
	fmt.Println(columnize.Format(content, config))
}

// GetAppScheduler fetches the scheduler for a given application
func GetAppScheduler(appName string) string {
	appScheduler := ""
	globalScheduler := ""

	var wg sync.WaitGroup

	if appName != "--global" {
		wg.Add(1)
		go func() {
			defer wg.Done()
			appScheduler = getAppScheduler(appName)
		}()
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		globalScheduler = GetGlobalScheduler()
	}()

	wg.Wait()

	if appScheduler == "" {
		appScheduler = globalScheduler
	}
	return appScheduler
}

func getAppScheduler(appName string) string {
	b, _ := PlugnTriggerOutput("config-get", []string{appName, "DOKKU_SCHEDULER"}...)
	value := strings.TrimSpace(string(b[:]))
	if value != "" {
		return value
	}
	return ""
}

// GetGlobalScheduler fetchs the global scheduler
func GetGlobalScheduler() string {
	b, _ := PlugnTriggerOutput("config-get-global", []string{"DOKKU_SCHEDULER"}...)
	value := strings.TrimSpace(string(b[:]))
	if value != "" {
		return value
	}

	return "docker-local"
}

// GetDeployingAppImageName returns deploying image identifier for a given app, tag tuple. validate if tag is presented
func GetDeployingAppImageName(appName, imageTag, imageRepo string) (string, error) {
	b, err := PlugnTriggerOutput("deployed-app-repository", []string{appName}...)
	if err != nil {
		return "", err
	}
	imageRemoteRepository := strings.TrimSpace(string(b[:]))

	b, err = PlugnTriggerOutput("deployed-app-image-tag", []string{appName}...)
	if err != nil {
		return "", err
	}
	newImageTag := strings.TrimSpace(string(b[:]))

	b, err = PlugnTriggerOutput("deployed-app-image-repo", []string{appName}...)
	if err != nil {
		return "", err
	}
	newImageRepo := strings.TrimSpace(string(b[:]))

	if newImageRepo != "" {
		imageRepo = newImageRepo
	}
	if newImageTag != "" {
		imageTag = newImageTag
	}
	if imageRepo == "" {
		imageRepo = GetAppImageRepo(appName)
	}
	if imageTag == "" {
		imageTag = "latest"
	}

	imageName := fmt.Sprintf("%s%s:%s", imageRemoteRepository, imageRepo, imageTag)
	if !VerifyImage(imageName) {
		return "", fmt.Errorf("App image (%s) not found", imageName)
	}
	return imageName, nil
}

// GetAppImageRepo is the central definition of a dokku image repo pattern
func GetAppImageRepo(appName string) string {
	return strings.Join([]string{"dokku", appName}, "/")
}

// GetAppContainerIDs returns a list of docker container ids for given app and optional container_type
func GetAppContainerIDs(appName string, containerType string) ([]string, error) {
	var containerIDs []string
	appRoot := AppRoot(appName)
	containerFilePath := fmt.Sprintf("%v/CONTAINER", appRoot)
	_, err := os.Stat(containerFilePath)
	if !os.IsNotExist(err) {
		containerIDs = append(containerIDs, ReadFirstLine(containerFilePath))
	}

	containerPattern := fmt.Sprintf("%v/CONTAINER.*", appRoot)
	if containerType != "" {
		containerPattern = fmt.Sprintf("%v/CONTAINER.%v.*", appRoot, containerType)
		if strings.Contains(".", containerType) {
			containerPattern = fmt.Sprintf("%v/CONTAINER.%v", appRoot, containerType)
		}
	}

	files, _ := filepath.Glob(containerPattern)
	for _, containerFile := range files {
		containerIDs = append(containerIDs, ReadFirstLine(containerFile))
	}

	return containerIDs, nil
}

// GetAppRunningContainerIDs return a list of running docker container ids for given app and optional container_type
func GetAppRunningContainerIDs(appName string, containerType string) ([]string, error) {
	var runningContainerIDs []string
	if !IsDeployed(appName) {
		LogFail(fmt.Sprintf("App %v has not been deployed", appName))
	}

	containerIDs, err := GetAppContainerIDs(appName, containerType)
	if err != nil {
		return runningContainerIDs, nil
	}
	for _, containerID := range containerIDs {
		if ContainerIsRunning(containerID) {
			runningContainerIDs = append(runningContainerIDs, containerID)
		}
	}

	return runningContainerIDs, nil
}

// GetRunningImageTag retrieves current image tag for a given app and returns empty string if no deployed containers are found
func GetRunningImageTag(appName string) (string, error) {
	containerIDs, err := GetAppContainerIDs(appName, "")
	if err != nil {
		return "", err
	}

	for _, containerID := range containerIDs {
		if image, err := DockerInspect(containerID, "{{ .Config.Image }}"); err == nil {
			return strings.Split(image, ":")[1], nil
		}
	}

	return "", errors.New("No image tag found")
}

// DokkuApps returns a list of all local apps
func DokkuApps() (apps []string, err error) {
	dokkuRoot := MustGetEnv("DOKKU_ROOT")
	files, err := ioutil.ReadDir(dokkuRoot)
	if err != nil {
		err = fmt.Errorf("You haven't deployed any applications yet")
		return
	}

	for _, f := range files {
		appRoot := AppRoot(f.Name())
		if !DirectoryExists(appRoot) {
			continue
		}
		if strings.HasPrefix(f.Name(), ".") {
			continue
		}
		apps = append(apps, f.Name())
	}

	if len(apps) == 0 {
		err = fmt.Errorf("You haven't deployed any applications yet")
		return
	}

	return
}

// GetAppImageName returns image identifier for a given app, tag tuple. validate if tag is presented
func GetAppImageName(appName, imageTag, imageRepo string) (imageName string) {
	if imageRepo == "" {
		imageRepo = GetAppImageRepo(appName)
	}

	if imageTag == "" {
		imageName = fmt.Sprintf("%v:latest", imageRepo)
	} else {
		imageName = fmt.Sprintf("%v:%v", imageRepo, imageTag)
		if !VerifyImage(imageName) {
			LogFail(fmt.Sprintf("App image (%s) not found", imageName))
		}
	}
	return
}

// IsDeployed returns true if given app has a running container
func IsDeployed(appName string) bool {
	scheduler := GetAppScheduler(appName)
	_, err := PlugnTriggerOutput("scheduler-is-deployed", []string{scheduler, appName}...)
	return err == nil
}

// MustGetEnv returns env variable or fails if it's not set
func MustGetEnv(key string) (val string) {
	val = os.Getenv(key)
	if val == "" {
		LogFail(fmt.Sprintf("%s not set!", key))
	}
	return
}

// GetenvWithDefault returns env variable or defaultValue if it's not set
func GetenvWithDefault(key string, defaultValue string) (val string) {
	val = os.Getenv(key)
	if val == "" {
		val = defaultValue
	}
	return
}

// ParseReportArgs splits out flags from non-flags for input into report commands
func ParseReportArgs(pluginName string, arguments []string) ([]string, string, error) {
	osArgs := []string{}
	infoFlags := []string{}
	for _, argument := range arguments {
		if strings.HasPrefix(argument, "--") {
			infoFlags = append(infoFlags, argument)
		} else {
			osArgs = append(osArgs, argument)
		}
	}

	if len(infoFlags) == 0 {
		return osArgs, "", nil
	}
	if len(infoFlags) == 1 {
		return osArgs, infoFlags[0], nil
	}
	return osArgs, "", fmt.Errorf("%s:report command allows only a single flag", pluginName)
}

// ReportSingleApp is an internal function that displays a report for an app
func ReportSingleApp(reportType string, appName string, infoFlag string, infoFlags map[string]string, trimPrefix bool, uppercaseFirstCharacter bool) error {
	flags := []string{}
	for key := range infoFlags {
		flags = append(flags, key)
	}
	sort.Strings(flags)

	if len(infoFlag) == 0 {
		LogInfo2Quiet(fmt.Sprintf("%s %v information", appName, reportType))
		for _, k := range flags {
			v := infoFlags[k]
			prefix := "--"
			if trimPrefix {
				prefix = fmt.Sprintf("--%v-", reportType)
			}

			key := strings.Replace(strings.Replace(strings.TrimPrefix(k, prefix), "-", " ", -1), ".", " ", -1)

			if uppercaseFirstCharacter {
				key = UcFirst(key)
			}

			LogVerbose(fmt.Sprintf("%s%s", RightPad(fmt.Sprintf("%s:", key), 31, " "), v))
		}
		return nil
	}

	for _, k := range flags {
		if infoFlag == k {
			v := infoFlags[k]
			fmt.Println(v)
			return nil
		}
	}

	keys := reflect.ValueOf(infoFlags).MapKeys()
	strkeys := make([]string, len(keys))
	for i := 0; i < len(keys); i++ {
		strkeys[i] = keys[i].String()
	}

	return fmt.Errorf("Invalid flag passed, valid flags: %s", strings.Join(strkeys, ", "))
}

// RightPad right-pads the string with pad up to len runes
func RightPad(str string, length int, pad string) string {
	return str + times(pad, length-len(str))
}

// ShiftString removes the first and returns that entry as well as the rest of the list
func ShiftString(a []string) (string, []string) {
	if len(a) == 0 {
		return "", a
	}

	return a[0], a[1:]
}

// StripInlineComments removes bash-style comment from input line
func StripInlineComments(text string) string {
	b := []byte(text)
	re := regexp.MustCompile("(?s)#.*")
	b = re.ReplaceAll(b, nil)
	return strings.TrimSpace(string(b))
}

// SuppressOutput suppresses the output of a function unless there is an error
func SuppressOutput(f errfunc) error {
	rescueStdout := os.Stdout
	r, w, _ := os.Pipe()
	os.Stdout = w

	err := f()

	w.Close()
	out, _ := ioutil.ReadAll(r)
	os.Stdout = rescueStdout

	if err != nil {
		fmt.Printf(string(out[:]))
	}

	return err
}

// ToBool returns a bool value for a given string
func ToBool(s string) bool {
	return s == "true"
}

// ToInt returns an int value for a given string
func ToInt(s string, defaultValue int) int {
	i, err := strconv.Atoi(s)
	if err != nil {
		return defaultValue
	}

	return i
}

// UcFirst uppercases the first character in a string
func UcFirst(str string) string {
	for i, v := range str {
		return string(unicode.ToUpper(v)) + str[i+1:]
	}
	return ""
}

// IsValidAppName verifies that the app name matches naming restrictions
func IsValidAppName(appName string) error {
	if appName == "" {
		return errors.New("Please specify an app to run the command on")
	}

	r, _ := regexp.Compile("^[a-z0-9][^/:_A-Z]*$")
	if r.MatchString(appName) {
		return nil
	}

	return errors.New("App name must begin with lowercase alphanumeric character, and cannot include uppercase characters, colons, or underscores")
}

// isValidAppNameOld verifies that the app name matches the old naming restrictions
func isValidAppNameOld(appName string) error {
	if appName == "" {
		return errors.New("Please specify an app to run the command on")
	}

	r, _ := regexp.Compile("^[a-z0-9][^/:A-Z]*$")
	if r.MatchString(appName) {
		return nil
	}

	return errors.New("App name must begin with lowercase alphanumeric character, and cannot include uppercase characters, or colons")
}

// VerifyAppName checks if an app conforming to either the old or new
// naming conventions exists
func VerifyAppName(appName string) error {
	newErr := IsValidAppName(appName)
	oldErr := isValidAppNameOld(appName)
	if newErr != nil && oldErr != nil {
		return newErr
	}

	appRoot := AppRoot(appName)
	if !DirectoryExists(appRoot) {
		return fmt.Errorf("App %s does not exist", appName)
	}

	return nil
}

func times(str string, n int) (out string) {
	for i := 0; i < n; i++ {
		out += str
	}
	return
}
<|endoftext|>


<commit_before>package xerrors

import (
	"context"
	"errors"
	"fmt"
	"github.com/stretchr/testify/assert"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"
	"time"
)

type subError struct {
	err error
	str string
}

func (err subError) Error() string {
	return fmt.Sprintf("%s(%s)", err.str, err.err.Error())
}

type subErrorPtr struct {
	err *error
	str string
}

func (err subErrorPtr) Error() string {
	return fmt.Sprintf("%s(%s)", err.str, (*err.err).Error())
}

func TestGetErrorInt(t *testing.T) {
	assert := assert.New(t)

	assert.Equal(nil, getError(5))

}
func TestGetErrorString(t *testing.T) {
	assert := assert.New(t)

	assert.Equal(nil, getError("hi"))
}
func TestGetErrorNil(t *testing.T) {
	assert := assert.New(t)

	assert.Equal(nil, getError(nil))
}

func TestGetErrorError(t *testing.T) {
	assert := assert.New(t)

	expected := errors.New("hi")
	assert.Equal(expected, getError(expected))
}
func TestGetErrorErrorPTR(t *testing.T) {
	assert := assert.New(t)

	expected := errors.New("hi")
	assert.Equal(expected, getError(&expected))
}

func TestGetErrorErrorComplex(t *testing.T) {
	assert := assert.New(t)
	type testA struct {
		error
		str string
	}
	expected := testA{errors.New("hi"), "bye"}
	assert.Equal(expected, getError(expected))
}

func TestGetErrorErrorComplexWithPointer(t *testing.T) {
	assert := assert.New(t)
	type testA struct {
		error
		str string
	}
	expected := testA{errors.New("hi"), "bye"}
	assert.Equal(expected, getError(&expected))
}

func TestGetErrorNoError(t *testing.T) {
	assert := assert.New(t)
	type testA struct {
		str string
	}
	expected := testA{"bye"}
	assert.Nil(getError(&expected))
}

func TestFirstCauseCustomSubError(t *testing.T) {
	assert := assert.New(t)
	type testA struct {
		error
		str string
	}
	exectedErr := errors.New("testA")
	test := testA{exectedErr, "cool"}
	assert.Equal(exectedErr, FirstCause(subError{test, "neat"}))
}

func TestFirstCauseNil(t *testing.T) {
	assert := assert.New(t)

	assert.Nil(FirstCause(nil))
}

func TestFirstCauseChainSubError(t *testing.T) {
	assert := assert.New(t)

	exectedErr := errors.New("expected error")
	test := subError{
		subError{
			subError{
				subErrorPtr{&exectedErr, "cool"},
				"c",
			},
			"b",
		},
		"a",
	}
	assert.Equal(exectedErr, FirstCause(subError{test, "root"}))
}

func TestGetErrorSubError(t *testing.T) {
	assert := assert.New(t)

	expected := subError{errors.New("hi"), "bye"}
	assert.Equal(expected, getError(&expected))
}

func TestFirstCauseBasic(t *testing.T) {
	assert := assert.New(t)

	err := errors.New("my bad")
	assert.Equal(err, FirstCause(err))
}

func testFirstCauseHTTPHandler(t *testing.T, serverSleep time.Duration, contextDeadline time.Duration, timeout time.Duration, useDefer bool) {
	assert := assert.New(t)

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		time.Sleep(serverSleep)
		fmt.Fprintln(w, "Hello, client")
	}))
	defer ts.Close()

	client := http.Client{Timeout: timeout}
	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
	assert.NoError(err)

	ctx, cancel := context.WithTimeout(context.Background(), contextDeadline)
	if useDefer {
		defer cancel()
	} else {
		cancel() // cancel() is a hook to cancel the deadline
	}

	reqWithDeadline := req.WithContext(ctx)

	_, clientErr := client.Do(reqWithDeadline)

	if !assert.Error(clientErr) {
		assert.FailNow("clientErr can not be nil to continue")
	}

	xerr := FirstCause(clientErr)
	assert.Equal(clientErr.(*url.Error).Err, xerr)
}

func TestFirstCauseHTTP(t *testing.T) {

	testData := []struct {
		name            string
		serverSleep     time.Duration
		contextDeadline time.Duration
		timeout         time.Duration
		useDefer        bool
	}{
		{"client-timeout", time.Second, 5 * time.Millisecond, time.Millisecond, true},
		{"context-cancel", time.Nanosecond, 5 * time.Millisecond, time.Millisecond, false},
		{"context-timeout", time.Second, time.Millisecond, 5 * time.Millisecond, true},
	}

	for _, record := range testData {
		t.Run(fmt.Sprintf("handle/%s", record.name), func(t *testing.T) {
			testFirstCauseHTTPHandler(t, record.serverSleep, record.contextDeadline, record.timeout, record.useDefer)
		})
	}
}
<commit_msg>Increased time for timeout error to focus on context-timeout<commit_after>package xerrors

import (
	"context"
	"errors"
	"fmt"
	"github.com/stretchr/testify/assert"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"
	"time"
)

type subError struct {
	err error
	str string
}

func (err subError) Error() string {
	return fmt.Sprintf("%s(%s)", err.str, err.err.Error())
}

type subErrorPtr struct {
	err *error
	str string
}

func (err subErrorPtr) Error() string {
	return fmt.Sprintf("%s(%s)", err.str, (*err.err).Error())
}

func TestGetErrorInt(t *testing.T) {
	assert := assert.New(t)

	assert.Equal(nil, getError(5))

}
func TestGetErrorString(t *testing.T) {
	assert := assert.New(t)

	assert.Equal(nil, getError("hi"))
}
func TestGetErrorNil(t *testing.T) {
	assert := assert.New(t)

	assert.Equal(nil, getError(nil))
}

func TestGetErrorError(t *testing.T) {
	assert := assert.New(t)

	expected := errors.New("hi")
	assert.Equal(expected, getError(expected))
}
func TestGetErrorErrorPTR(t *testing.T) {
	assert := assert.New(t)

	expected := errors.New("hi")
	assert.Equal(expected, getError(&expected))
}

func TestGetErrorErrorComplex(t *testing.T) {
	assert := assert.New(t)
	type testA struct {
		error
		str string
	}
	expected := testA{errors.New("hi"), "bye"}
	assert.Equal(expected, getError(expected))
}

func TestGetErrorErrorComplexWithPointer(t *testing.T) {
	assert := assert.New(t)
	type testA struct {
		error
		str string
	}
	expected := testA{errors.New("hi"), "bye"}
	assert.Equal(expected, getError(&expected))
}

func TestGetErrorNoError(t *testing.T) {
	assert := assert.New(t)
	type testA struct {
		str string
	}
	expected := testA{"bye"}
	assert.Nil(getError(&expected))
}

func TestFirstCauseCustomSubError(t *testing.T) {
	assert := assert.New(t)
	type testA struct {
		error
		str string
	}
	exectedErr := errors.New("testA")
	test := testA{exectedErr, "cool"}
	assert.Equal(exectedErr, FirstCause(subError{test, "neat"}))
}

func TestFirstCauseNil(t *testing.T) {
	assert := assert.New(t)

	assert.Nil(FirstCause(nil))
}

func TestFirstCauseChainSubError(t *testing.T) {
	assert := assert.New(t)

	exectedErr := errors.New("expected error")
	test := subError{
		subError{
			subError{
				subErrorPtr{&exectedErr, "cool"},
				"c",
			},
			"b",
		},
		"a",
	}
	assert.Equal(exectedErr, FirstCause(subError{test, "root"}))
}

func TestGetErrorSubError(t *testing.T) {
	assert := assert.New(t)

	expected := subError{errors.New("hi"), "bye"}
	assert.Equal(expected, getError(&expected))
}

func TestFirstCauseBasic(t *testing.T) {
	assert := assert.New(t)

	err := errors.New("my bad")
	assert.Equal(err, FirstCause(err))
}

func testFirstCauseHTTPHandler(t *testing.T, serverSleep time.Duration, contextDeadline time.Duration, timeout time.Duration, useDefer bool) {
	assert := assert.New(t)

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		time.Sleep(serverSleep)
		fmt.Fprintln(w, "Hello, client")
	}))
	defer ts.Close()

	client := http.Client{Timeout: timeout}
	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
	assert.NoError(err)

	ctx, cancel := context.WithTimeout(context.Background(), contextDeadline)
	if useDefer {
		defer cancel()
	} else {
		cancel() // cancel() is a hook to cancel the deadline
	}

	reqWithDeadline := req.WithContext(ctx)

	_, clientErr := client.Do(reqWithDeadline)

	if !assert.Error(clientErr) {
		assert.FailNow("clientErr can not be nil to continue")
	}

	xerr := FirstCause(clientErr)
	assert.Equal(clientErr.(*url.Error).Err, xerr)
}

func TestFirstCauseHTTP(t *testing.T) {

	testData := []struct {
		name            string
		serverSleep     time.Duration
		contextDeadline time.Duration
		timeout         time.Duration
		useDefer        bool
	}{
		{"client-timeout", time.Second, 500 * time.Millisecond, time.Millisecond, true},
		{"context-cancel", time.Nanosecond, 500 * time.Millisecond, time.Millisecond, false},
		{"context-timeout", time.Second, time.Millisecond, 500 * time.Millisecond, true},
	}

	for _, record := range testData {
		t.Run(fmt.Sprintf("handle/%s", record.name), func(t *testing.T) {
			testFirstCauseHTTPHandler(t, record.serverSleep, record.contextDeadline, record.timeout, record.useDefer)
		})
	}
}
<|endoftext|>