<commit_before>package cribbage

import (
	"bufio"
	"fmt"
	"github.com/pkg/errors"
	"github.com/whereswaldon/cryptage/card"
	"log"
	"os"
	"strconv"
	"strings"
)

type ScoreBoard struct {
	p1current, p1last, p2current, p2last uint
}

type Cribbage struct {
	deck                Deck
	opponent            Opponent
	players             int
	playerNum           int
	dealer              int
	hand, crib          *Hand
	currentState        State
	cutCard             *Card
	stateChangeRequests chan func()
}

type Deck interface {
	Draw(uint) (card.CardFace, error)
	Quit()
	Start([]card.CardFace) error
	Size() uint
}

type Opponent interface {
	Send(message []byte) error
	Recieve() <-chan []byte
}

func NewCribbage(deck Deck, opp Opponent, playerNum int) (*Cribbage, error) {
	if deck == nil {
		return nil, fmt.Errorf("Cannot create Cribbage with nil deck")
	} else if opp == nil {
		return nil, fmt.Errorf("Cannot create Cribbage with nil opponent")
	} else if playerNum < 1 || playerNum > 2 {
		return nil, fmt.Errorf("Illegal playerNum %d", playerNum)
	}

	cribbage := &Cribbage{
		deck:                deck,
		players:             2,
		playerNum:           playerNum,
		opponent:            opp,
		dealer:              1, //player 1 is always first dealer
		crib:                &Hand{cards: make([]*Card, 0), indicies: make([]uint, 0)},
		currentState:        DRAW_STATE,
		stateChangeRequests: make(chan func()),
	}
	go func() {
		for req := range cribbage.stateChangeRequests {
			req()
		}
	}()
	go cribbage.listenToMessages()
	return cribbage, nil
}

func (c *Cribbage) listenToMessages() {
	for bytes := range c.opponent.Recieve() {
		m, err := Decode(bytes)
		if err != nil {
			log.Println("Error decoding application message:", err)
		}
		switch m.Type {
		case TO_CRIB:
			log.Println("Recieved TO_CRIB")
			c.addIndexToCrib(m.Val)
		case CUT_CARD:
			log.Println("Recieved CUT_CARD")
			c.setCutCard(m.Val)
		default:
			log.Println("Unrecognized message type:", m.Type)
		}
	}
}

// sendToCribMsg sends the opponent a message informing them of which card
// the local player has opted to add to the crib. The value sent is an absolute
// index into the deck, rather than into the local player's hand.
func (c *Cribbage) sentToCribMsg(deckIndex uint) error {
	enc, err := Encode(&Message{Type: TO_CRIB, Val: deckIndex})
	if err != nil {
		return err
	}
	return c.opponent.Send(enc)
}

// sendCutCardMsg sends the opponent a message informing them of which card
// the local player is cutting as the shared card

func (c *Cribbage) sentCutCardMsg(deckIndex uint) error {
	enc, err := Encode(&Message{Type: CUT_CARD, Val: deckIndex})
	if err != nil {
		return err
	}
	return c.opponent.Send(enc)
}

func (c *Cribbage) drawHand() (*Hand, error) {
	out := make(chan struct {
		*Hand
		error
	})
	c.stateChangeRequests <- func() {
		handSize := getHandSize(c.players)
		c.hand = &Hand{
			cards:    make([]*Card, handSize),
			indicies: make([]uint, handSize),
		}
		var index uint
		for i := range c.hand.cards {
			if c.playerNum == 1 {
				index = 2 * uint(i)
			} else if c.playerNum == 2 {
				index = 2*uint(i) + 1
			} else {
				out <- struct {
					*Hand
					error
				}{nil, fmt.Errorf("Unsupported player number %d", c.playerNum)}
				return
			}

			current, err := c.deck.Draw(index)
			if err != nil {
				out <- struct {
					*Hand
					error
				}{nil, errors.Wrapf(err, "Unable to get hand")}
				return
			}
			c.hand.indicies[i] = index
			c.hand.cards[i] = &Card{}
			c.hand.cards[i].UnmarshalText(current)
		}
		out <- struct {
			*Hand
			error
		}{c.hand, nil}
	}
	temp := <-out
	return temp.Hand, temp.error
}

// Hand returns the local player's hand
func (c *Cribbage) Hand() (*Hand, error) {
	if c.hand == nil {
		return c.drawHand()
	}
	return c.hand, nil
}

func (c *Cribbage) Quit() error {
	c.deck.Quit()
	return nil
}

// addIndexToCrib adds the card at the given deck index to the crib.
// This is primarily useful for adding the opponent's selections to
// the local crib, since the local player does not know the faces
// of those cards
func (c *Cribbage) addIndexToCrib(deckIndex uint) error {
	if deckIndex >= c.deck.Size() {
		return fmt.Errorf("Index out of bounds")
	}
	errs := make(chan error)
	c.stateChangeRequests <- func() {
		c.crib.cards = append(c.crib.cards, nil)
		c.crib.indicies = append(c.crib.indicies, deckIndex)
		errs <- nil
	}
	return <-errs
}

func (c *Cribbage) setCutCard(deckIndex uint) error {
	if deckIndex > c.deck.Size() {
		return fmt.Errorf("Index out of bounds")
	} else if deckIndex < 12 {
		return fmt.Errorf("Tried to set cut card to a card in a current player's hand")
	}
	errs := make(chan error)
	c.stateChangeRequests <- func() {
		card, err := c.deck.Draw(deckIndex)
		if err != nil {
			errs <- err
			return
		}
		decCard := &Card{}
		err = decCard.UnmarshalText(card)
		if err != nil {
			errs <- err
			return
		}
		c.cutCard = decCard
	}
	return <-errs
}

// Crib adds the card at the specified index within the player's hand to the
// crib. This remove it from the player's hand.
func (c *Cribbage) Crib(handIndex uint) error {
	//ensure hand has been initialized
	c.Hand()
	errs := make(chan error)
	c.stateChangeRequests <- func() {
		if handIndex >= uint(len(c.hand.cards)) {
			errs <- fmt.Errorf("Index out of bounds %d", handIndex)
			return
		}
		lastIndex := len(c.hand.cards) - 1
		if lastIndex < 4 {
			errs <- fmt.Errorf("Cannot add another card to crib, hand is already minimum size")
			return
		}
		c.sentToCribMsg(c.hand.indicies[handIndex])
		c.crib.cards = append(c.crib.cards, c.hand.cards[handIndex])
		c.crib.indicies = append(c.crib.indicies, c.hand.indicies[handIndex])
		c.hand.cards[handIndex] = c.hand.cards[lastIndex]
		c.hand.indicies[handIndex] = c.hand.indicies[lastIndex]
		c.hand.cards = c.hand.cards[:lastIndex]
		c.hand.indicies = c.hand.indicies[:lastIndex]
		errs <- nil
	}
	return <-errs
}

// Cut attempts to cut the deck at the specified card
func (c *Cribbage) Cut(deckIndex uint) error {
	errs := make(chan error)
	c.stateChangeRequests <- func() {
		if deckIndex >= uint(c.deck.Size()) {
			errs <- fmt.Errorf("Index out of bounds %d", deckIndex)
			return
		} else if deckIndex < 12 { //cutting into cards that have been dealt
			errs <- fmt.Errorf("Cannot cut at index %d, cards 0-12 are in player hands.", deckIndex)
		}
		c.sentCutCardMsg(deckIndex)
		card, err := c.deck.Draw(deckIndex)
		if err != nil {
			errs <- err
			return
		}
		decodedCard := &Card{}
		err = decodedCard.UnmarshalText(card)
		if err != nil {
			errs <- err
			return
		}
		c.cutCard = decodedCard
		errs <- nil
	}
	return <-errs
}

func (c *Cribbage) updateState() {
	done := make(chan struct{})
	c.stateChangeRequests <- func() {
		defer close(done)
		switch c.currentState {
		case DRAW_STATE:
			if c.hand != nil {
				c.currentState = DISCARD_STATE
			}
		case DISCARD_STATE:
			if len(c.hand.cards) == 4 {
				c.currentState = DISCARD_WAIT_STATE
			}
		case DISCARD_WAIT_STATE:
			if len(c.crib.cards) == 4 {
				if c.dealer == c.playerNum {
					c.currentState = CUT_WAIT_STATE
				} else {
					c.currentState = CUT_STATE
				}
			}
		case CUT_STATE:
			if c.cutCard != nil {
				c.currentState = CIRCULAR_STATE
			}
		case CUT_WAIT_STATE:
			if c.cutCard != nil {
				c.currentState = CIRCULAR_STATE
			}
		case CIRCULAR_STATE:
		case INTERNAL_STATE:
		case CRIB_STATE:
		case END_STATE:
		}
	}
	<-done
}

func (c *Cribbage) UI() {
	scanner := bufio.NewScanner(os.Stdin)
	for {
		c.updateState()
		fmt.Println(instructionsForState(c.currentState))
		fmt.Print("> ")
		scanner.Scan()
		input := strings.Split(strings.TrimSpace(scanner.Text()), " ")
		switch input[0] {
		case "quit":
			c.Quit()
			return
		case "hand":
			h, err := c.Hand()
			if err != nil {
				fmt.Println(err)
			} else {
				fmt.Println("hand: ", RenderHand(h))
			}
		case "toCrib":
			if len(input) < 2 {
				fmt.Println("Usage: toCrib <card-index>")
				continue
			}
			i, err := strconv.Atoi(input[1])
			if err != nil {
				fmt.Println("Not a valid card index! Use numbers next time")
				continue
			}
			err = c.Crib(uint(i))
			if err != nil {
				fmt.Printf("error adding %s to crib: %v\n", input[1], err)
				continue
			}
			fmt.Println("crib: ", RenderHand(c.crib))
		case "crib":
			fmt.Println("crib: ", RenderHand(c.crib))
		case "cut":
			if c.cutCard != nil {
				fmt.Println("cut: ", RenderCard(c.cutCard))
			} else {
				fmt.Println("No cut card yet")
			}
		case "cutAt":
			if len(input) < 2 {
				fmt.Println("Usage: cutAt <card-index>")
				continue
			}
			i, err := strconv.Atoi(input[1])
			if err != nil {
				fmt.Println("Not a valid card index! Use numbers next time")
				continue
			}
			err = c.Cut(uint(i))
			if err != nil {
				fmt.Printf("error cutting card %d: %v", i, err)
				continue
			}
			fmt.Println("cut: ", RenderCard(c.cutCard))
		case "help":
			fmt.Println(STR_HELP)
		default:
			fmt.Println("Uknown command: ", input[0])
			fmt.Println(STR_HELP)
		}
	}
}
<commit_msg>Ensure that all errors in closures also return<commit_after>package cribbage

import (
	"bufio"
	"fmt"
	"github.com/pkg/errors"
	"github.com/whereswaldon/cryptage/card"
	"log"
	"os"
	"strconv"
	"strings"
)

type ScoreBoard struct {
	p1current, p1last, p2current, p2last uint
}

type Cribbage struct {
	deck                Deck
	opponent            Opponent
	players             int
	playerNum           int
	dealer              int
	hand, crib          *Hand
	currentState        State
	cutCard             *Card
	stateChangeRequests chan func()
}

type Deck interface {
	Draw(uint) (card.CardFace, error)
	Quit()
	Start([]card.CardFace) error
	Size() uint
}

type Opponent interface {
	Send(message []byte) error
	Recieve() <-chan []byte
}

func NewCribbage(deck Deck, opp Opponent, playerNum int) (*Cribbage, error) {
	if deck == nil {
		return nil, fmt.Errorf("Cannot create Cribbage with nil deck")
	} else if opp == nil {
		return nil, fmt.Errorf("Cannot create Cribbage with nil opponent")
	} else if playerNum < 1 || playerNum > 2 {
		return nil, fmt.Errorf("Illegal playerNum %d", playerNum)
	}

	cribbage := &Cribbage{
		deck:                deck,
		players:             2,
		playerNum:           playerNum,
		opponent:            opp,
		dealer:              1, //player 1 is always first dealer
		crib:                &Hand{cards: make([]*Card, 0), indicies: make([]uint, 0)},
		currentState:        DRAW_STATE,
		stateChangeRequests: make(chan func()),
	}
	go func() {
		for req := range cribbage.stateChangeRequests {
			req()
		}
	}()
	go cribbage.listenToMessages()
	return cribbage, nil
}

func (c *Cribbage) listenToMessages() {
	for bytes := range c.opponent.Recieve() {
		m, err := Decode(bytes)
		if err != nil {
			log.Println("Error decoding application message:", err)
		}
		switch m.Type {
		case TO_CRIB:
			log.Println("Recieved TO_CRIB")
			c.addIndexToCrib(m.Val)
		case CUT_CARD:
			log.Println("Recieved CUT_CARD")
			c.setCutCard(m.Val)
		default:
			log.Println("Unrecognized message type:", m.Type)
		}
	}
}

// sendToCribMsg sends the opponent a message informing them of which card
// the local player has opted to add to the crib. The value sent is an absolute
// index into the deck, rather than into the local player's hand.
func (c *Cribbage) sentToCribMsg(deckIndex uint) error {
	enc, err := Encode(&Message{Type: TO_CRIB, Val: deckIndex})
	if err != nil {
		return err
	}
	return c.opponent.Send(enc)
}

// sendCutCardMsg sends the opponent a message informing them of which card
// the local player is cutting as the shared card

func (c *Cribbage) sentCutCardMsg(deckIndex uint) error {
	enc, err := Encode(&Message{Type: CUT_CARD, Val: deckIndex})
	if err != nil {
		return err
	}
	return c.opponent.Send(enc)
}

func (c *Cribbage) drawHand() (*Hand, error) {
	out := make(chan struct {
		*Hand
		error
	})
	c.stateChangeRequests <- func() {
		handSize := getHandSize(c.players)
		c.hand = &Hand{
			cards:    make([]*Card, handSize),
			indicies: make([]uint, handSize),
		}
		var index uint
		for i := range c.hand.cards {
			if c.playerNum == 1 {
				index = 2 * uint(i)
			} else if c.playerNum == 2 {
				index = 2*uint(i) + 1
			} else {
				out <- struct {
					*Hand
					error
				}{nil, fmt.Errorf("Unsupported player number %d", c.playerNum)}
				return
			}

			current, err := c.deck.Draw(index)
			if err != nil {
				out <- struct {
					*Hand
					error
				}{nil, errors.Wrapf(err, "Unable to get hand")}
				return
			}
			c.hand.indicies[i] = index
			c.hand.cards[i] = &Card{}
			c.hand.cards[i].UnmarshalText(current)
		}
		out <- struct {
			*Hand
			error
		}{c.hand, nil}
	}
	temp := <-out
	return temp.Hand, temp.error
}

// Hand returns the local player's hand
func (c *Cribbage) Hand() (*Hand, error) {
	if c.hand == nil {
		return c.drawHand()
	}
	return c.hand, nil
}

func (c *Cribbage) Quit() error {
	c.deck.Quit()
	return nil
}

// addIndexToCrib adds the card at the given deck index to the crib.
// This is primarily useful for adding the opponent's selections to
// the local crib, since the local player does not know the faces
// of those cards
func (c *Cribbage) addIndexToCrib(deckIndex uint) error {
	if deckIndex >= c.deck.Size() {
		return fmt.Errorf("Index out of bounds")
	}
	errs := make(chan error)
	c.stateChangeRequests <- func() {
		c.crib.cards = append(c.crib.cards, nil)
		c.crib.indicies = append(c.crib.indicies, deckIndex)
		errs <- nil
	}
	return <-errs
}

func (c *Cribbage) setCutCard(deckIndex uint) error {
	if deckIndex > c.deck.Size() {
		return fmt.Errorf("Index out of bounds")
	} else if deckIndex < 12 {
		return fmt.Errorf("Tried to set cut card to a card in a current player's hand")
	}
	errs := make(chan error)
	c.stateChangeRequests <- func() {
		card, err := c.deck.Draw(deckIndex)
		if err != nil {
			errs <- err
			return
		}
		decCard := &Card{}
		err = decCard.UnmarshalText(card)
		if err != nil {
			errs <- err
			return
		}
		c.cutCard = decCard
	}
	return <-errs
}

// Crib adds the card at the specified index within the player's hand to the
// crib. This remove it from the player's hand.
func (c *Cribbage) Crib(handIndex uint) error {
	//ensure hand has been initialized
	c.Hand()
	errs := make(chan error)
	c.stateChangeRequests <- func() {
		if c.currentState != DISCARD_STATE && c.currentState != DRAW_STATE {
			fmt.Errorf("You can't discard to the crib right now")
			return
		} else if handIndex >= uint(len(c.hand.cards)) {
			errs <- fmt.Errorf("Index out of bounds %d", handIndex)
			return
		}
		lastIndex := len(c.hand.cards) - 1
		if lastIndex < 4 {
			errs <- fmt.Errorf("Cannot add another card to crib, hand is already minimum size")
			return
		}
		c.sentToCribMsg(c.hand.indicies[handIndex])
		c.crib.cards = append(c.crib.cards, c.hand.cards[handIndex])
		c.crib.indicies = append(c.crib.indicies, c.hand.indicies[handIndex])
		c.hand.cards[handIndex] = c.hand.cards[lastIndex]
		c.hand.indicies[handIndex] = c.hand.indicies[lastIndex]
		c.hand.cards = c.hand.cards[:lastIndex]
		c.hand.indicies = c.hand.indicies[:lastIndex]
		errs <- nil
	}
	return <-errs
}

// Cut attempts to cut the deck at the specified card
func (c *Cribbage) Cut(deckIndex uint) error {
	errs := make(chan error)
	c.stateChangeRequests <- func() {
		if c.currentState != CUT_STATE {
			errs <- fmt.Errorf("You can't cut the deck right now")
			return
		} else if deckIndex >= uint(c.deck.Size()) {
			errs <- fmt.Errorf("Index out of bounds %d", deckIndex)
			return
		} else if deckIndex < 12 { //cutting into cards that have been dealt
			errs <- fmt.Errorf("Cannot cut at index %d, cards 0-12 are in player hands.", deckIndex)
			return
		}
		c.sentCutCardMsg(deckIndex)
		card, err := c.deck.Draw(deckIndex)
		if err != nil {
			errs <- err
			return
		}
		decodedCard := &Card{}
		err = decodedCard.UnmarshalText(card)
		if err != nil {
			errs <- err
			return
		}
		c.cutCard = decodedCard
		errs <- nil
	}
	return <-errs
}

func (c *Cribbage) updateState() {
	done := make(chan struct{})
	c.stateChangeRequests <- func() {
		defer close(done)
		switch c.currentState {
		case DRAW_STATE:
			if c.hand != nil {
				c.currentState = DISCARD_STATE
			}
		case DISCARD_STATE:
			if len(c.hand.cards) == 4 {
				c.currentState = DISCARD_WAIT_STATE
			}
		case DISCARD_WAIT_STATE:
			if len(c.crib.cards) == 4 {
				if c.dealer == c.playerNum {
					c.currentState = CUT_WAIT_STATE
				} else {
					c.currentState = CUT_STATE
				}
			}
		case CUT_STATE:
			if c.cutCard != nil {
				c.currentState = CIRCULAR_STATE
			}
		case CUT_WAIT_STATE:
			if c.cutCard != nil {
				c.currentState = CIRCULAR_STATE
			}
		case CIRCULAR_STATE:
		case INTERNAL_STATE:
		case CRIB_STATE:
		case END_STATE:
		}
	}
	<-done
}

func (c *Cribbage) UI() {
	scanner := bufio.NewScanner(os.Stdin)
	for {
		c.updateState()
		fmt.Println(instructionsForState(c.currentState))
		fmt.Print("> ")
		scanner.Scan()
		input := strings.Split(strings.TrimSpace(scanner.Text()), " ")
		switch input[0] {
		case "quit":
			c.Quit()
			return
		case "hand":
			h, err := c.Hand()
			if err != nil {
				fmt.Println(err)
			} else {
				fmt.Println("hand: ", RenderHand(h))
			}
		case "toCrib":
			if len(input) < 2 {
				fmt.Println("Usage: toCrib <card-index>")
				continue
			}
			i, err := strconv.Atoi(input[1])
			if err != nil {
				fmt.Println("Not a valid card index! Use numbers next time")
				continue
			}
			err = c.Crib(uint(i))
			if err != nil {
				fmt.Printf("error adding %s to crib: %v\n", input[1], err)
				continue
			}
			fmt.Println("crib: ", RenderHand(c.crib))
		case "crib":
			fmt.Println("crib: ", RenderHand(c.crib))
		case "cut":
			if c.cutCard != nil {
				fmt.Println("cut: ", RenderCard(c.cutCard))
			} else {
				fmt.Println("No cut card yet")
			}
		case "cutAt":
			if len(input) < 2 {
				fmt.Println("Usage: cutAt <card-index>")
				continue
			}
			i, err := strconv.Atoi(input[1])
			if err != nil {
				fmt.Println("Not a valid card index! Use numbers next time")
				continue
			}
			err = c.Cut(uint(i))
			if err != nil {
				fmt.Printf("error cutting card %d: %v", i, err)
				continue
			}
			fmt.Println("cut: ", RenderCard(c.cutCard))
		case "help":
			fmt.Println(STR_HELP)
		default:
			fmt.Println("Uknown command: ", input[0])
			fmt.Println(STR_HELP)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Niklas Wolber
// This file is licensed under the MIT license.
// See the LICENSE file for more information.

package job

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/nwolber/xCUTEr/flunc"

	"context"
)

// ExecutionTree creates the execution tree necessary to executeCommand
// the configured steps.
func (c *Config) ExecutionTree() (flunc.Flunc, error) {
	f, err := visitConfig(&executionTreeVisitor{}, c)
	if err != nil {
		return nil, err
	}

	return f.(flunc.Flunc), nil
}

type executionGroup struct {
	fluncs []flunc.Flunc
	group  func(...flunc.Flunc) flunc.Flunc
}

func (g *executionGroup) Append(children ...interface{}) {
	for _, cc := range children {
		if cc == nil {
			continue
		}

		f, ok := cc.(flunc.Flunc)
		if !ok {
			log.Panicf("not a flunc %T", cc)
		}

		g.fluncs = append(g.fluncs, f)
	}
}

func (g *executionGroup) Wrap() interface{} {
	return g.group(g.fluncs...)
}

type executionTreeVisitor struct{}

func (e *executionTreeVisitor) Sequential() group {
	return &executionGroup{group: flunc.Sequential}
}

func (e *executionTreeVisitor) Parallel() group {
	return &executionGroup{group: flunc.Parallel}
}

func makeFlunc(f flunc.Flunc) flunc.Flunc {
	return f
}

func (e *executionTreeVisitor) Job(name string) group {
	return e.Sequential()
}

func (*executionTreeVisitor) Output(file string) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		output, _ := ctx.Value(outputKey).(io.Writer)

		if file == "" {
			if output != nil {
				return context.WithValue(ctx, outputKey, io.MultiWriter(os.Stdout, output)), nil
			}
			return context.WithValue(ctx, outputKey, os.Stdout), nil
		}

		tt, ok := ctx.Value(templatingKey).(*templatingEngine)
		if !ok {
			err := fmt.Errorf("no %s available", templatingKey)
			log.Println(err)
			return nil, err
		}

		file, err := tt.Interpolate(file)
		if err != nil {
			log.Println("error parsing template string", file, err)
			return nil, err
		}

		f, err := os.OpenFile(file, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.FileMode(0644))
		if err != nil {
			err = fmt.Errorf("unable to open job output file %s %s", file, err)
			return nil, err
		}

		fmt.Fprintln(f)
		fmt.Fprintln(f)
		fmt.Fprintf(f, "============ %s ============\n", time.Now())

		go func(ctx context.Context, f io.Closer) {
			<-ctx.Done()
			f.Close()
			log.Println("closed job output file", file)
		}(ctx, f)

		if output != nil {
			return context.WithValue(ctx, outputKey, io.MultiWriter(f, output)), nil
		}

		return context.WithValue(ctx, outputKey, f), nil
	})
}

func (e *executionTreeVisitor) JobLogger(jobName string) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		output, ok := ctx.Value(outputKey).(io.Writer)
		if !ok {
			err := fmt.Errorf("no %s available", outputKey)
			log.Println(err)
			return nil, err
		}

		return context.WithValue(ctx, loggerKey, log.New(output, jobName+": ", log.Flags())), nil
	})
}

func (e *executionTreeVisitor) HostLogger(jobName string, h *host) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		output, ok := ctx.Value(outputKey).(io.Writer)
		if !ok {
			err := fmt.Errorf("no %s available", outputKey)
			log.Println(err)
			return nil, err
		}

		logger := log.New(output, fmt.Sprintf("%s - %s: ", jobName, h.Name), log.Flags())
		logger.Println("logger created")
		return context.WithValue(ctx, loggerKey, logger), nil
	})
}

func (e *executionTreeVisitor) Timeout(timeout time.Duration) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		ctx, _ = context.WithTimeout(ctx, timeout)
		l.Println("set timeout to", timeout)
		return ctx, nil
	})
}

func (e *executionTreeVisitor) SCP(scp *scp) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		b, err := ioutil.ReadFile(scp.Key)
		if err != nil {
			l.Println("failed reading key file", err)
			return nil, err
		}

		addr := fmt.Sprintf("%s:%d", scp.Addr, scp.Port)
		l.Println("setting up scp on", addr)
		doSCP(ctx, b, addr)
		return nil, nil
	})
}

func (e *executionTreeVisitor) Hosts() group {
	return e.Parallel()
}

func (e *executionTreeVisitor) Host(c *Config, h *host) group {
	return e.Sequential()
}

func (e *executionTreeVisitor) ErrorSafeguard(child interface{}) interface{} {
	f, ok := child.(flunc.Flunc)
	if !ok {
		log.Panicf("not a flunc %T", child)
	}

	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		ctx, err := f(ctx)
		if err != nil {
			l.Println(err)
			return nil, nil
		}
		return ctx, nil
	})
}

func (e *executionTreeVisitor) ContextBounds(child interface{}) interface{} {
	f, ok := child.(flunc.Flunc)
	if !ok {
		log.Panicf("not a flunc %T", child)
	}

	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		ctx, cancel := context.WithCancel(ctx)
		defer cancel()

		_, err := f(ctx)
		return nil, err
	})
}

func (e *executionTreeVisitor) Templating(c *Config, h *host) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		tt := newTemplatingEngine(c, h)
		return context.WithValue(ctx, templatingKey, tt), nil
	})
}

func (*executionTreeVisitor) SSHClient(host, user, keyFile, password string, keyboardInteractive map[string]string) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		l.Println("connecting to ", host)
		s, err := newSSHClient(ctx, host, user, keyFile, password, keyboardInteractive)
		if err != nil {
			l.Println("ssh client setup failed", err)
			return nil, err
		}
		l.Println("connected to ", host)

		return context.WithValue(ctx, sshClientKey, s), nil
	})
}

func (*executionTreeVisitor) Forwarding(f *forwarding) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		s, ok := ctx.Value(sshClientKey).(*sshClient)
		if !ok {
			return nil, fmt.Errorf("no %s available", sshClientKey)
		}

		remoteAddr := fmt.Sprintf("%s:%d", f.RemoteHost, f.RemotePort)
		localAddr := fmt.Sprintf("%s:%d", f.LocalHost, f.LocalPort)
		l.Println("setting up forwarding", remoteAddr, "->", localAddr)
		s.forward(ctx, remoteAddr, localAddr)

		return nil, nil
	})
}

func (e *executionTreeVisitor) Commands(cmd *command) group {
	return e.Sequential()
}

func (*executionTreeVisitor) Command(cmd *command) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		s, ok := ctx.Value(sshClientKey).(*sshClient)
		if !ok {
			return nil, fmt.Errorf("no %s available", sshClientKey)
		}

		tt, ok := ctx.Value(templatingKey).(*templatingEngine)
		if !ok {
			err := fmt.Errorf("no %s available", templatingKey)
			log.Println(err)
			return nil, err
		}

		command, err := tt.Interpolate(cmd.Command)
		if err != nil {
			l.Println("error parsing template string", cmd.Command, err)
			return nil, err
		}

		stdout, _ := ctx.Value(stdoutKey).(io.Writer)
		stderr, _ := ctx.Value(stderrKey).(io.Writer)
		err = s.executeCommand(ctx, command, stdout, stderr)
		return nil, err
	})
}

func (*executionTreeVisitor) LocalCommand(cmd *command) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		tt, ok := ctx.Value(templatingKey).(*templatingEngine)
		if !ok {
			err := fmt.Errorf("no %s available", templatingKey)
			log.Println(err)
			return nil, err
		}

		command, err := tt.Interpolate(cmd.Command)
		if err != nil {
			l.Println("error parsing template string", cmd.Command, err)
			return nil, err
		}

		parts := strings.Split(command, " ")
		exe := parts[0]
		args := parts[1:]

		stdout, _ := ctx.Value(stdoutKey).(io.Writer)
		stderr, _ := ctx.Value(stderrKey).(io.Writer)

		// processTerminated := make(chan error)
		// cmd := exec.Command(exe, args...)
		cmd := exec.CommandContext(ctx, exe, args...)
		cmd.Stdout = stdout
		cmd.Stderr = stderr

		l.Println("executing local command", command)
		// go func(cmd *exec.Cmd, c chan<- error) {
		if err := cmd.Run(); err != nil {
			l.Printf("error running %q locally: %s", command, err)
			return nil, err
		}
		l.Println("%q completed successfully", command)
		return nil, nil
	})
}

func (e *executionTreeVisitor) Stdout(file string) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		tt, ok := ctx.Value(templatingKey).(*templatingEngine)
		if !ok {
			err := fmt.Errorf("no %s available", templatingKey)
			log.Println(err)
			return nil, err
		}

		path, err := tt.Interpolate(file)
		if err != nil {
			l.Println("error parsing template string", file, err)
			return nil, err
		}

		f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.FileMode(0644))
		if err != nil {
			err = fmt.Errorf("unable to open stdout file: %s", err)
			l.Println(err)
			return nil, err
		}
		l.Println("opened", path, "for stdout")

		fmt.Fprintln(f)
		fmt.Fprintln(f)
		fmt.Fprintf(f, "============ %s ============\n", time.Now())

		go func(ctx context.Context, f io.Closer, path string) {
			<-ctx.Done()
			l.Println("closing stdout", path)
			f.Close()
		}(ctx, f, path)

		return context.WithValue(ctx, stdoutKey, f), nil
	})
}

func (*executionTreeVisitor) Stderr(file string) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		tt, ok := ctx.Value(templatingKey).(*templatingEngine)
		if !ok {
			err := fmt.Errorf("no %s available", templatingKey)
			log.Println(err)
			return nil, err
		}

		path, err := tt.Interpolate(file)
		if err != nil {
			l.Println("error parsing template string", file, err)
			return nil, err
		}

		f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.FileMode(0644))
		if err != nil {
			err = fmt.Errorf("unable to open stdout file: %s", err)
			l.Println(err)
			return nil, err
		}
		l.Println("opened", path, "for stderr")

		fmt.Fprintln(f)
		fmt.Fprintln(f)
		fmt.Fprintf(f, "============ %s ============\n", time.Now())

		go func(ctx context.Context, f io.Closer, path string) {
			<-ctx.Done()
			l.Println("closing stderr", path)
			f.Close()
		}(ctx, f, path)

		return context.WithValue(ctx, stderrKey, f), nil
	})
}
<commit_msg>Remove unused code<commit_after>// Copyright (c) 2016 Niklas Wolber
// This file is licensed under the MIT license.
// See the LICENSE file for more information.

package job

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/nwolber/xCUTEr/flunc"

	"context"
)

// ExecutionTree creates the execution tree necessary to executeCommand
// the configured steps.
func (c *Config) ExecutionTree() (flunc.Flunc, error) {
	f, err := visitConfig(&executionTreeVisitor{}, c)
	if err != nil {
		return nil, err
	}

	return f.(flunc.Flunc), nil
}

type executionGroup struct {
	fluncs []flunc.Flunc
	group  func(...flunc.Flunc) flunc.Flunc
}

func (g *executionGroup) Append(children ...interface{}) {
	for _, cc := range children {
		if cc == nil {
			continue
		}

		f, ok := cc.(flunc.Flunc)
		if !ok {
			log.Panicf("not a flunc %T", cc)
		}

		g.fluncs = append(g.fluncs, f)
	}
}

func (g *executionGroup) Wrap() interface{} {
	return g.group(g.fluncs...)
}

type executionTreeVisitor struct{}

func (e *executionTreeVisitor) Sequential() group {
	return &executionGroup{group: flunc.Sequential}
}

func (e *executionTreeVisitor) Parallel() group {
	return &executionGroup{group: flunc.Parallel}
}

func makeFlunc(f flunc.Flunc) flunc.Flunc {
	return f
}

func (e *executionTreeVisitor) Job(name string) group {
	return e.Sequential()
}

func (*executionTreeVisitor) Output(file string) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		output, _ := ctx.Value(outputKey).(io.Writer)

		if file == "" {
			if output != nil {
				return context.WithValue(ctx, outputKey, io.MultiWriter(os.Stdout, output)), nil
			}
			return context.WithValue(ctx, outputKey, os.Stdout), nil
		}

		tt, ok := ctx.Value(templatingKey).(*templatingEngine)
		if !ok {
			err := fmt.Errorf("no %s available", templatingKey)
			log.Println(err)
			return nil, err
		}

		file, err := tt.Interpolate(file)
		if err != nil {
			log.Println("error parsing template string", file, err)
			return nil, err
		}

		f, err := os.OpenFile(file, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.FileMode(0644))
		if err != nil {
			err = fmt.Errorf("unable to open job output file %s %s", file, err)
			return nil, err
		}

		fmt.Fprintln(f)
		fmt.Fprintln(f)
		fmt.Fprintf(f, "============ %s ============\n", time.Now())

		go func(ctx context.Context, f io.Closer) {
			<-ctx.Done()
			f.Close()
			log.Println("closed job output file", file)
		}(ctx, f)

		if output != nil {
			return context.WithValue(ctx, outputKey, io.MultiWriter(f, output)), nil
		}

		return context.WithValue(ctx, outputKey, f), nil
	})
}

func (e *executionTreeVisitor) JobLogger(jobName string) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		output, ok := ctx.Value(outputKey).(io.Writer)
		if !ok {
			err := fmt.Errorf("no %s available", outputKey)
			log.Println(err)
			return nil, err
		}

		return context.WithValue(ctx, loggerKey, log.New(output, jobName+": ", log.Flags())), nil
	})
}

func (e *executionTreeVisitor) HostLogger(jobName string, h *host) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		output, ok := ctx.Value(outputKey).(io.Writer)
		if !ok {
			err := fmt.Errorf("no %s available", outputKey)
			log.Println(err)
			return nil, err
		}

		logger := log.New(output, fmt.Sprintf("%s - %s: ", jobName, h.Name), log.Flags())
		logger.Println("logger created")
		return context.WithValue(ctx, loggerKey, logger), nil
	})
}

func (e *executionTreeVisitor) Timeout(timeout time.Duration) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		ctx, _ = context.WithTimeout(ctx, timeout)
		l.Println("set timeout to", timeout)
		return ctx, nil
	})
}

func (e *executionTreeVisitor) SCP(scp *scp) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		b, err := ioutil.ReadFile(scp.Key)
		if err != nil {
			l.Println("failed reading key file", err)
			return nil, err
		}

		addr := fmt.Sprintf("%s:%d", scp.Addr, scp.Port)
		l.Println("setting up scp on", addr)
		doSCP(ctx, b, addr)
		return nil, nil
	})
}

func (e *executionTreeVisitor) Hosts() group {
	return e.Parallel()
}

func (e *executionTreeVisitor) Host(c *Config, h *host) group {
	return e.Sequential()
}

func (e *executionTreeVisitor) ErrorSafeguard(child interface{}) interface{} {
	f, ok := child.(flunc.Flunc)
	if !ok {
		log.Panicf("not a flunc %T", child)
	}

	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		ctx, err := f(ctx)
		if err != nil {
			l.Println(err)
			return nil, nil
		}
		return ctx, nil
	})
}

func (e *executionTreeVisitor) ContextBounds(child interface{}) interface{} {
	f, ok := child.(flunc.Flunc)
	if !ok {
		log.Panicf("not a flunc %T", child)
	}

	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		ctx, cancel := context.WithCancel(ctx)
		defer cancel()

		_, err := f(ctx)
		return nil, err
	})
}

func (e *executionTreeVisitor) Templating(c *Config, h *host) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		tt := newTemplatingEngine(c, h)
		return context.WithValue(ctx, templatingKey, tt), nil
	})
}

func (*executionTreeVisitor) SSHClient(host, user, keyFile, password string, keyboardInteractive map[string]string) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		l.Println("connecting to ", host)
		s, err := newSSHClient(ctx, host, user, keyFile, password, keyboardInteractive)
		if err != nil {
			l.Println("ssh client setup failed", err)
			return nil, err
		}
		l.Println("connected to ", host)

		return context.WithValue(ctx, sshClientKey, s), nil
	})
}

func (*executionTreeVisitor) Forwarding(f *forwarding) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		s, ok := ctx.Value(sshClientKey).(*sshClient)
		if !ok {
			return nil, fmt.Errorf("no %s available", sshClientKey)
		}

		remoteAddr := fmt.Sprintf("%s:%d", f.RemoteHost, f.RemotePort)
		localAddr := fmt.Sprintf("%s:%d", f.LocalHost, f.LocalPort)
		l.Println("setting up forwarding", remoteAddr, "->", localAddr)
		s.forward(ctx, remoteAddr, localAddr)

		return nil, nil
	})
}

func (e *executionTreeVisitor) Commands(cmd *command) group {
	return e.Sequential()
}

func (*executionTreeVisitor) Command(cmd *command) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		s, ok := ctx.Value(sshClientKey).(*sshClient)
		if !ok {
			return nil, fmt.Errorf("no %s available", sshClientKey)
		}

		tt, ok := ctx.Value(templatingKey).(*templatingEngine)
		if !ok {
			err := fmt.Errorf("no %s available", templatingKey)
			log.Println(err)
			return nil, err
		}

		command, err := tt.Interpolate(cmd.Command)
		if err != nil {
			l.Println("error parsing template string", cmd.Command, err)
			return nil, err
		}

		stdout, _ := ctx.Value(stdoutKey).(io.Writer)
		stderr, _ := ctx.Value(stderrKey).(io.Writer)
		err = s.executeCommand(ctx, command, stdout, stderr)
		return nil, err
	})
}

func (*executionTreeVisitor) LocalCommand(cmd *command) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		tt, ok := ctx.Value(templatingKey).(*templatingEngine)
		if !ok {
			err := fmt.Errorf("no %s available", templatingKey)
			log.Println(err)
			return nil, err
		}

		command, err := tt.Interpolate(cmd.Command)
		if err != nil {
			l.Println("error parsing template string", cmd.Command, err)
			return nil, err
		}

		parts := strings.Split(command, " ")
		exe := parts[0]
		args := parts[1:]

		stdout, _ := ctx.Value(stdoutKey).(io.Writer)
		stderr, _ := ctx.Value(stderrKey).(io.Writer)

		cmd := exec.CommandContext(ctx, exe, args...)
		cmd.Stdout = stdout
		cmd.Stderr = stderr

		l.Println("executing local command", command)
		if err := cmd.Run(); err != nil {
			l.Printf("error running %q locally: %s", command, err)
			return nil, err
		}
		l.Printf("%q completed successfully", command)
		return nil, nil
	})
}

func (e *executionTreeVisitor) Stdout(file string) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		tt, ok := ctx.Value(templatingKey).(*templatingEngine)
		if !ok {
			err := fmt.Errorf("no %s available", templatingKey)
			log.Println(err)
			return nil, err
		}

		path, err := tt.Interpolate(file)
		if err != nil {
			l.Println("error parsing template string", file, err)
			return nil, err
		}

		f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.FileMode(0644))
		if err != nil {
			err = fmt.Errorf("unable to open stdout file: %s", err)
			l.Println(err)
			return nil, err
		}
		l.Println("opened", path, "for stdout")

		fmt.Fprintln(f)
		fmt.Fprintln(f)
		fmt.Fprintf(f, "============ %s ============\n", time.Now())

		go func(ctx context.Context, f io.Closer, path string) {
			<-ctx.Done()
			l.Println("closing stdout", path)
			f.Close()
		}(ctx, f, path)

		return context.WithValue(ctx, stdoutKey, f), nil
	})
}

func (*executionTreeVisitor) Stderr(file string) interface{} {
	return makeFlunc(func(ctx context.Context) (context.Context, error) {
		l, ok := ctx.Value(loggerKey).(*log.Logger)
		if !ok {
			err := fmt.Errorf("no %s available", loggerKey)
			log.Println(err)
			return nil, err
		}

		tt, ok := ctx.Value(templatingKey).(*templatingEngine)
		if !ok {
			err := fmt.Errorf("no %s available", templatingKey)
			log.Println(err)
			return nil, err
		}

		path, err := tt.Interpolate(file)
		if err != nil {
			l.Println("error parsing template string", file, err)
			return nil, err
		}

		f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.FileMode(0644))
		if err != nil {
			err = fmt.Errorf("unable to open stdout file: %s", err)
			l.Println(err)
			return nil, err
		}
		l.Println("opened", path, "for stderr")

		fmt.Fprintln(f)
		fmt.Fprintln(f)
		fmt.Fprintf(f, "============ %s ============\n", time.Now())

		go func(ctx context.Context, f io.Closer, path string) {
			<-ctx.Done()
			l.Println("closing stderr", path)
			f.Close()
		}(ctx, f, path)

		return context.WithValue(ctx, stderrKey, f), nil
	})
}
<|endoftext|>


<commit_before>package kick

import (
	"github.com/spf13/cobra"
	"fmt"
	"github.com/BluePecker/JwtAuth/engine/client"
)

func NewCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "kick",
		Short: "force users to go offline",
		RunE: func(cmd *cobra.Command, args []string) error {
			unixSock, err := cmd.Parent().Flags().GetString("unix-sock")
			if err != nil {
				return err
			}
			cli := client.NewClient(unixSock)
			if _, err := cli.Get("/v1.0/token/kick"); err != nil {
				return err
			}
			fmt.Printf("successfully kicked out the user.\n")
			return nil
		},
	}
	return cmd
}
<commit_msg>fix bug<commit_after>package kick

import (
	"github.com/spf13/cobra"
	"fmt"
	"github.com/BluePecker/JwtAuth/engine/client"
)

func NewCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "kick",
		Short: "force users to go offline",
		RunE: func(cmd *cobra.Command, args []string) error {
			unixSock, err := cmd.Parent().Flags().GetString("unix-sock")
			if err != nil {
				return err
			}
			cli := client.NewClient(unixSock)
			if body, err := cli.Get("/v1.0/token/kick"); err != nil {
				return err
			} else {
				fmt.Println(body)
				fmt.Printf("successfully kicked out the user.\n")
				return nil
			}
		},
	}
	return cmd
}
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.519"
<commit_msg>fnserver: 0.3.520 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.520"
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.346"
<commit_msg>fnserver: 0.3.347 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.347"
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.565"
<commit_msg>fnserver: 0.3.566 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.566"
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.92"
<commit_msg>functions: 0.3.93 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.93"
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.422"
<commit_msg>fnserver: 0.3.423 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.423"
<|endoftext|>


<commit_before>package getexif

import (
	"bytes"
	"container/list"
	"encoding/json"
	"errors"
	"fmt"
	"os/exec"
	"path"
	"sync"
	"sync/atomic"

	"github.com/kevintavog/findaphoto/common"
	"github.com/kevintavog/findaphoto/indexer/steps/preparemedia"

	"github.com/ian-kent/go-log/log"
)

var ExifToolInvocations int64
var ExifToolFailed int64

type ExifForDirectory struct {
	Directory string
	Files     *list.List
	lock      sync.Mutex
	dequeued  bool
	Exif      []*common.ExifOutput
}

const numConsumers = 8

var queue = make(chan *ExifForDirectory, numConsumers)
var waitGroup sync.WaitGroup
var allDirectories map[string]*ExifForDirectory
var allLock sync.Mutex

func Start() {
	preparemedia.Start()

	allDirectories = make(map[string]*ExifForDirectory)

	waitGroup.Add(numConsumers)
	for idx := 0; idx < numConsumers; idx++ {
		go func() {
			dequeue()
			waitGroup.Done()
		}()
	}
}

func Done() {
	close(queue)
}

func Wait() {
	waitGroup.Wait()
	preparemedia.Done()
	preparemedia.Wait()
}

func Enqueue(candidate *common.CandidateFile) {
	directory := path.Dir(candidate.FullPath)

	var inQueue bool
	var exifForDirectory *ExifForDirectory

	// Lock only to check if this has been queued & added to allDirectories
	{
		allLock.Lock()
		defer allLock.Unlock()

		if exifForDirectory, inQueue = allDirectories[directory]; !inQueue {
			exifForDirectory = &ExifForDirectory{
				Directory: directory,
				Files:     list.New(),
				dequeued:  false,
			}

			allDirectories[directory] = exifForDirectory
		}
	}

	// Add this file to the exif for the directory
	{
		exifForDirectory.lock.Lock()
		defer exifForDirectory.lock.Unlock()

		if !exifForDirectory.dequeued {
			exifForDirectory.Files.PushBack(candidate)
		} else {
			ex, err := exifForFile(exifForDirectory, path.Base(candidate.FullPath))
			if err != nil {
				candidate.Exif = *ex
			}
			preparemedia.Enqueue(candidate)
		}
	}

	if !inQueue {
		queue <- exifForDirectory
	}
}

func dequeue() {
	for exifForDirectory := range queue {

		atomic.AddInt64(&ExifToolInvocations, 1)

		exifOutput, err := getDirectoryExif(exifForDirectory.Directory)
		if err != nil {
			atomic.AddInt64(&ExifToolFailed, 1)
			continue
		}

		exifForDirectory.Exif = exifOutput

		exifForDirectory.lock.Lock()
		defer exifForDirectory.lock.Unlock()

		exifForDirectory.dequeued = true

		for ele := exifForDirectory.Files.Front(); ele != nil; ele = ele.Next() {
			candidate := ele.Value.(*common.CandidateFile)
			ex, err := exifForFile(exifForDirectory, path.Base(candidate.FullPath))
			if err == nil {
				candidate.Exif = *ex
			} else {
				candidate.AddWarning(err.Error())
			}
			preparemedia.Enqueue(candidate)
		}
	}
}

func exifForFile(exifForDirectory *ExifForDirectory, filename string) (*common.ExifOutput, error) {
	if !exifForDirectory.dequeued {
		return nil, errors.New("No exif data available")
	}

	for idx, ex := range exifForDirectory.Exif {
		if filename == path.Base(ex.SourceFile) {

			exifForDirectory.Exif = append(exifForDirectory.Exif[:idx], exifForDirectory.Exif[idx+1:]...)
			directory := path.Dir(filename)
			delete(allDirectories, directory)

			return ex, nil
		}
	}

	return nil, errors.New(fmt.Sprintf("No exif for %s", filename))
}

func getDirectoryExif(directory string) ([]*common.ExifOutput, error) {
	out, err := exec.Command(common.ExifToolPath, "-a", "-j", "-g", "-x", "Directory", "-x", "FileAccessDate", "-x", "FileInodeChangeDate", directory).Output()
	if err != nil {
		log.Fatal("Failed executing exiftool for '%s': %s", directory, err.Error())
	}

	var response []*common.ExifOutput
	decoder := json.NewDecoder(bytes.NewReader(out))
	err = decoder.Decode(&response)
	if err != nil {
		log.Error("json decoder error for '%s': %s", directory, err.Error())
		return nil, err
	}

	return response, nil
}
<commit_msg>Lock the collection when deleting items from it - fixes concurrency panic<commit_after>package getexif

import (
	"bytes"
	"container/list"
	"encoding/json"
	"errors"
	"fmt"
	"os/exec"
	"path"
	"sync"
	"sync/atomic"

	"github.com/kevintavog/findaphoto/common"
	"github.com/kevintavog/findaphoto/indexer/steps/preparemedia"

	"github.com/ian-kent/go-log/log"
)

var ExifToolInvocations int64
var ExifToolFailed int64

type ExifForDirectory struct {
	Directory string
	Files     *list.List
	lock      sync.Mutex
	dequeued  bool
	Exif      []*common.ExifOutput
}

const numConsumers = 8

var queue = make(chan *ExifForDirectory, numConsumers)
var waitGroup sync.WaitGroup
var allDirectories map[string]*ExifForDirectory
var allLock sync.Mutex

func Start() {
	preparemedia.Start()

	allDirectories = make(map[string]*ExifForDirectory)

	waitGroup.Add(numConsumers)
	for idx := 0; idx < numConsumers; idx++ {
		go func() {
			dequeue()
			waitGroup.Done()
		}()
	}
}

func Done() {
	close(queue)
}

func Wait() {
	waitGroup.Wait()
	preparemedia.Done()
	preparemedia.Wait()
}

func Enqueue(candidate *common.CandidateFile) {
	directory := path.Dir(candidate.FullPath)

	var inQueue bool
	var exifForDirectory *ExifForDirectory

	// Lock only to check if this has been queued & added to allDirectories
	{
		allLock.Lock()
		defer allLock.Unlock()

		if exifForDirectory, inQueue = allDirectories[directory]; !inQueue {
			exifForDirectory = &ExifForDirectory{
				Directory: directory,
				Files:     list.New(),
				dequeued:  false,
			}

			allDirectories[directory] = exifForDirectory
		}
	}

	// Add this file to the exif for the directory
	{
		exifForDirectory.lock.Lock()
		defer exifForDirectory.lock.Unlock()

		if !exifForDirectory.dequeued {
			exifForDirectory.Files.PushBack(candidate)
		} else {
			ex, err := exifForFile(exifForDirectory, path.Base(candidate.FullPath))
			if err != nil {
				candidate.Exif = *ex
			}
			preparemedia.Enqueue(candidate)
		}
	}

	if !inQueue {
		queue <- exifForDirectory
	}
}

func dequeue() {
	for exifForDirectory := range queue {

		atomic.AddInt64(&ExifToolInvocations, 1)

		exifOutput, err := getDirectoryExif(exifForDirectory.Directory)
		if err != nil {
			atomic.AddInt64(&ExifToolFailed, 1)
			continue
		}

		exifForDirectory.Exif = exifOutput

		exifForDirectory.lock.Lock()
		defer exifForDirectory.lock.Unlock()

		exifForDirectory.dequeued = true

		for ele := exifForDirectory.Files.Front(); ele != nil; ele = ele.Next() {
			candidate := ele.Value.(*common.CandidateFile)
			ex, err := exifForFile(exifForDirectory, path.Base(candidate.FullPath))
			if err == nil {
				candidate.Exif = *ex
			} else {
				candidate.AddWarning(err.Error())
			}
			preparemedia.Enqueue(candidate)
		}
	}
}

func exifForFile(exifForDirectory *ExifForDirectory, filename string) (*common.ExifOutput, error) {
	if !exifForDirectory.dequeued {
		return nil, errors.New("No exif data available")
	}

	for idx, ex := range exifForDirectory.Exif {
		if filename == path.Base(ex.SourceFile) {

			exifForDirectory.Exif = append(exifForDirectory.Exif[:idx], exifForDirectory.Exif[idx+1:]...)
			directory := path.Dir(filename)

			allLock.Lock()
			defer allLock.Unlock()
			delete(allDirectories, directory)

			return ex, nil
		}
	}

	return nil, errors.New(fmt.Sprintf("No exif for %s", filename))
}

func getDirectoryExif(directory string) ([]*common.ExifOutput, error) {
	out, err := exec.Command(common.ExifToolPath, "-a", "-j", "-g", "-x", "Directory", "-x", "FileAccessDate", "-x", "FileInodeChangeDate", directory).Output()
	if err != nil {
		log.Fatal("Failed executing exiftool for '%s': %s", directory, err.Error())
	}

	var response []*common.ExifOutput
	decoder := json.NewDecoder(bytes.NewReader(out))
	err = decoder.Decode(&response)
	if err != nil {
		log.Error("json decoder error for '%s': %s", directory, err.Error())
		return nil, err
	}

	return response, nil
}
<|endoftext|>


<commit_before>package jobs

import (
	"strings"

	log "github.com/meifamily/logrus"
	"github.com/meifamily/ptt-alertor/models"
	"github.com/meifamily/ptt-alertor/models/article"
	"github.com/meifamily/ptt-alertor/models/author"
	"github.com/meifamily/ptt-alertor/models/board"
	"github.com/meifamily/ptt-alertor/models/keyword"
	"github.com/meifamily/ptt-alertor/models/pushsum"
)

type migrateBoard struct {
	boardMap map[string]string
}

func NewMigrateBoard(boardMap map[string]string) *migrateBoard {
	return &migrateBoard{boardMap: boardMap}
}

func (m migrateBoard) Run() {
	for pre, post := range m.boardMap {
		m.RunSingle(pre, post)
	}
}

func (migrateBoard) RunSingle(preBoard string, postBoard string) {
	// board list
	if postBoard != "" {
		addBoard(postBoard)
	}
	bd := board.NewBoard()
	bd.Name = preBoard
	bd.Delete()
	log.Info("Board List Migrated")

	// keyword
	if postBoard != "" {
		subs := keyword.Subscribers(preBoard)
		for _, sub := range subs {
			keyword.AddSubscriber(postBoard, sub)
		}
	}
	keyword.Destroy(preBoard)
	log.Info("Keyword Migrated")

	// author
	if postBoard != "" {
		subs := author.Subscribers(preBoard)
		for _, sub := range subs {
			author.AddSubscriber(postBoard, sub)
		}
	}
	author.Destroy(preBoard)
	log.Info("Author Migrated")

	// pushsum
	if postBoard != "" {
		pushsum.Add(postBoard)
		subs := pushsum.ListSubscribers(preBoard)
		for _, sub := range subs {
			pushsum.AddSubscriber(postBoard, sub)
		}
	}
	pushsum.Remove(preBoard)
	pushsum.Destroy(preBoard)
	pushsum.RenameDiffListKeys(preBoard, postBoard)
	log.Info("Pushsum Migrated")

	// articles
	codes := new(article.Articles).List()
	for _, code := range codes {
		a := new(article.Article).Find(code)
		if strings.EqualFold(a.Board, preBoard) {
			if postBoard == "" {
				a.Destroy()
			} else {
				a.Board = postBoard
				a.Save()
			}
			log.WithField("code", code).Info("Article Migrated")
		}
	}
	log.Info("Articles Migrated")

	// user
	for _, u := range models.User.All() {
		for _, sub := range u.Subscribes {
			if strings.EqualFold(sub.Board, preBoard) {
				u.Subscribes.Delete(sub)
				if postBoard != "" {
					for _, postSub := range u.Subscribes {
						if strings.EqualFold(postSub.Board, postBoard) {
							if postSub.PushSum.Up == 0 && postSub.PushSum.Down == 0 {
								postSub.PushSum.Up, postSub.PushSum.Down = sub.PushSum.Up, sub.PushSum.Down
								u.Subscribes.Update(postSub)
							}
						}
					}
					sub.Board = postBoard
					u.Subscribes.Add(sub)
				}
				u.Update()
				log.WithField("account", u.Account).Info("User Subscription Migrated")
			}
		}
	}
	log.Info("Board Migrated")
}
<commit_msg>:loud_sound: add board name in log<commit_after>package jobs

import (
	"strings"

	log "github.com/meifamily/logrus"
	"github.com/meifamily/ptt-alertor/models"
	"github.com/meifamily/ptt-alertor/models/article"
	"github.com/meifamily/ptt-alertor/models/author"
	"github.com/meifamily/ptt-alertor/models/board"
	"github.com/meifamily/ptt-alertor/models/keyword"
	"github.com/meifamily/ptt-alertor/models/pushsum"
)

type migrateBoard struct {
	boardMap map[string]string
}

func NewMigrateBoard(boardMap map[string]string) *migrateBoard {
	return &migrateBoard{boardMap: boardMap}
}

func (m migrateBoard) Run() {
	for pre, post := range m.boardMap {
		log.WithField("board", pre).Info("Board Migrating")
		m.RunSingle(pre, post)
	}
	log.Info("All Board Migrated")
}

func (migrateBoard) RunSingle(preBoard string, postBoard string) {
	// board list
	if postBoard != "" {
		addBoard(postBoard)
	}
	bd := board.NewBoard()
	bd.Name = preBoard
	bd.Delete()
	log.WithField("board", preBoard).Info("Board List Migrated")

	// keyword
	if postBoard != "" {
		subs := keyword.Subscribers(preBoard)
		for _, sub := range subs {
			keyword.AddSubscriber(postBoard, sub)
		}
	}
	keyword.Destroy(preBoard)
	log.WithField("board", preBoard).Info("Keyword Migrated")

	// author
	if postBoard != "" {
		subs := author.Subscribers(preBoard)
		for _, sub := range subs {
			author.AddSubscriber(postBoard, sub)
		}
	}
	author.Destroy(preBoard)
	log.WithField("board", preBoard).Info("Author Migrated")

	// pushsum
	if postBoard != "" {
		pushsum.Add(postBoard)
		subs := pushsum.ListSubscribers(preBoard)
		for _, sub := range subs {
			pushsum.AddSubscriber(postBoard, sub)
		}
	}
	pushsum.Remove(preBoard)
	pushsum.Destroy(preBoard)
	pushsum.RenameDiffListKeys(preBoard, postBoard)
	log.WithField("board", preBoard).Info("Pushsum Migrated")

	// articles
	codes := new(article.Articles).List()
	for _, code := range codes {
		a := new(article.Article).Find(code)
		if strings.EqualFold(a.Board, preBoard) {
			if postBoard == "" {
				a.Destroy()
			} else {
				a.Board = postBoard
				a.Save()
			}
			log.WithFields(log.Fields{
				"board": preBoard,
				"code":  code,
			}).Info("Article Migrated")
		}
	}
	log.Info("Articles Migrated")

	// user
	for _, u := range models.User.All() {
		for _, sub := range u.Subscribes {
			if strings.EqualFold(sub.Board, preBoard) {
				u.Subscribes.Delete(sub)
				if postBoard != "" {
					for _, postSub := range u.Subscribes {
						if strings.EqualFold(postSub.Board, postBoard) {
							if postSub.PushSum.Up == 0 && postSub.PushSum.Down == 0 {
								postSub.PushSum.Up, postSub.PushSum.Down = sub.PushSum.Up, sub.PushSum.Down
								u.Subscribes.Update(postSub)
							}
						}
					}
					sub.Board = postBoard
					u.Subscribes.Add(sub)
				}
				u.Update()
				log.WithFields(log.Fields{
					"account": u.Account,
					"board":   preBoard,
				}).Info("User Subscription Migrated")
			}
		}
	}
}
<|endoftext|>


<commit_before>package cuckoo

import (
	"bytes"
	"fmt"
	"math/rand"
	"strconv"
	"testing"
)

// Test identical state after operations
// Test contains after insert/remove sequence
// Test insert same value twice

func GetBytes(val string) []byte {
	buf := make([]byte, 64)
	copy(buf, bytes.NewBufferString(val).Bytes())
	return buf
}

func randBucket(numBuckets int) int {
	result := rand.Int() % numBuckets
	if result < 0 {
		result = result * -1
	}
	return result
}

func TestGetCapacity(t *testing.T) {
	fmt.Printf("TestGetCapacity: ...\n")

	table := NewTable("t", 10, 2, 64, nil, 0)
	if table.GetCapacity() != 20 {
		t.Fatalf("table returned wrong value for GetCapacity=%v. Expecting 20\n")
	}

	table = NewTable("t", 1, 1, 64, nil, 0)
	if table.GetCapacity() != 1 {
		t.Fatalf("table returned wrong value for GetCapacity=%v. Expecting 1\n")
	}

	table = NewTable("t", 0, 0, 64, nil, 0)
	if table.GetCapacity() != 0 {
		t.Fatalf("table returned wrong value for GetCapacity=%v. Expecting 0\n")
	}

	fmt.Printf("... done \n")
}

func TestBasic(t *testing.T) {
	table := NewTable("t", 10, 2, 64, nil, 0)

	fmt.Printf("TestBasic: check empty...\n")
	if table.GetNumElements() != 0 {
		t.Fatalf("empty table returned %v for GetNumElements()\n", table.GetNumElements())
	}

	fmt.Printf("TestBasic: Check contains non-existent value...\n")
	if table.Contains(&Item{0, GetBytes(""), 0, 1}) == true {
		t.Fatalf("empty table returned true for Contains()\n")
	}

	fmt.Printf("TestBasic: remove non-existent value ...\n")
	if table.Remove(&Item{1, GetBytes("value1"), 0, 1}) == true {
		t.Fatalf("empty table returned true for Remove()\n")
	}

	fmt.Printf("TestBasic: check empty...\n")
	if table.GetNumElements() != 0 {
		t.Fatalf("empty table returned %v for GetNumElements()\n", table.GetNumElements())
	}

	fmt.Printf("TestBasic: Insert value ...\n")
	ok, itm := table.Insert(&Item{1, GetBytes("value1"), 0, 1})
	if itm != nil || ok != true {
		t.Fatalf("error inserting into table (0, 1, value1)\n")
	}

	fmt.Printf("TestBasic: Check inserted value...\n")
	if table.Contains(&Item{1, GetBytes("value1"), 0, 1}) == false {
		t.Fatalf("cannot find recently inserted value\n")
	}

	fmt.Printf("TestBasic: Check inserted value w/o full reference...\n")
	if table.Contains(&Item{1, GetBytes(""), 0, 1}) == false {
		t.Fatalf("cannot find recently inserted value\n")
	}

	fmt.Printf("TestBasic: Check non-existent value...\n")
	if table.Contains(&Item{2, GetBytes("value2"), 0, 1}) == true {
		t.Fatalf("contains a non-existent value\n")
	}

	fmt.Printf("TestBasic: check 1 element...\n")
	if table.GetNumElements() != 1 {
		t.Fatalf("empty table returned %v for GetNumElements()\n", table.GetNumElements())
	}

	fmt.Printf("TestBasic: remove existing value ...\n")
	if table.Remove(&Item{1, GetBytes("value1"), 0, 1}) == false {
		t.Fatalf("error removing existing value (0, 1, value1)\n")
	}

	fmt.Printf("TestBasic: check 0 element...\n")
	if table.GetNumElements() != 0 {
		t.Fatalf("empty table returned %v for GetNumElements()\n", table.GetNumElements())
	}

	fmt.Printf("TestBasic: remove recently removed value ...\n")
	if table.Remove(&Item{1, GetBytes("value1"), 0, 1}) == true {
		t.Fatalf("empty table returned true for Remove()\n")
	}

	fmt.Printf("TestBasic: check 0 element...\n")
	if table.GetNumElements() != 0 {
		t.Fatalf("empty table returned %v for GetNumElements()\n", table.GetNumElements())
	}

	fmt.Printf("... done \n")
}

func TestOutOfBounds(t *testing.T) {
	table := NewTable("t", 10, 2, 64, nil, 0)

	fmt.Printf("TestOutOfBounds: Insert() out of bounds...\n")
	ok, itm := table.Insert(&Item{1, GetBytes("value1"), 100, 100})
	if ok == true {
		t.Fatalf("Insert returned true with out of bound buckets\n")
	}
	if itm != nil {
		t.Fatalf("Insert returned wrong values\n")
	}

	fmt.Printf("TestOutOfBounds: Contains() out of bounds...\n")
	if table.Contains(&Item{1, GetBytes("value1"), 100, 100}) == true {
		t.Fatalf("Contains returned true with out of bound buckets\n")
	}

	fmt.Printf("TestOutOfBounds: Remove() out of bounds...\n")
	if table.Remove(&Item{1, GetBytes("value1"), 100, 100}) == true {
		t.Fatalf("Remove() returned true with out of bound buckets\n")
	}

	fmt.Printf("... done \n")
}

func TestFullTable(t *testing.T) {
	numBuckets := 100
	depth := 4

	capacity := numBuckets * depth
	entries := make([]Item, 0, capacity)
	table := NewTable("t", numBuckets, depth, 64, nil, 0)
	ok := true
	count := 0
	var evic *Item
	var b1, b2 int

	// Insert random values until we've reached a limit
	fmt.Printf("TestFullTable: Insert until reach capacity...\n")
	for ok {
		b1 = randBucket(numBuckets)
		b2 = randBucket(numBuckets)
		id := rand.Int()
		val := GetBytes(strconv.Itoa(rand.Int()))
		entries = append(entries, Item{id, nil, b1, b2})
		ok, evic = table.Insert(&Item{id, val, b1, b2})

		if ok {
			count += 1
			if table.Contains(&Item{id, nil, b1, b2}) == false {
				t.Fatalf("Insert() succeeded, but Contains failed\n")
			}
			if count != table.GetNumElements() {
				t.Fatalf("Number of successful Inserts(), %v, does not match GetNumElements(), %v \n", count, table.GetNumElements())
			}
		}
	}

	// Middle count check
	fmt.Printf("TestFullTable: Fully Loaded check...\n")
	if count != table.GetNumElements() {
		t.Fatalf("Number of successful Inserts(), %v, does not match GetNumElements(), %v \n", count, table.GetNumElements())
	}
	maxCount := count

	// Remove elements one by one
	fmt.Printf("TestFullTable: Remove each element...\n")
	for _, entry := range entries {
		if !entry.Equals(evic) {
			ok = table.Remove(&entry)
			if ok == false {
				t.Fatalf("Cannot Remove() an element believed to be in the table. item %d of %d", count, maxCount)
			} else {
				count -= 1
				if count != table.GetNumElements() {
					t.Fatalf("GetNumElements()=%v returned a value that didn't match what was expected=%v \n", table.GetNumElements(), count)
				}
			}
		}
	}

	// Final count check
	fmt.Printf("TestFullTable: Final count check...\n")
	if table.GetNumElements() != 0 {
		t.Fatalf("GetNumElements() returns %v when table should be empty \n", table.GetNumElements())
	}

	fmt.Printf("... done\n")
}

func TestDuplicateValues(t *testing.T) {
	fmt.Printf("TestDuplicateValues: ...\n")
	table := NewTable("t", 10, 2, 64, nil, 0)

	ok, itm := table.Insert(&Item{1, GetBytes("v"), 0, 1})
	if itm != nil || ok == false {
		t.Fatalf("Error inserting value \n")
	}

	ok, itm = table.Insert(&Item{2, GetBytes("v"), 0, 1})
	if itm != nil || ok == false {
		t.Fatalf("Error inserting value again \n")
	}

	ok, itm = table.Insert(&Item{3, GetBytes("v"), 1, 2})
	if itm != nil || ok == false {
		t.Fatalf("Error inserting value in shifted buckets\n")
	}

	if table.Remove(&Item{1, GetBytes("v"), 0, 1}) == false {
		t.Fatalf("Error removing value 1st time\n")
	}

	if table.Remove(&Item{2, GetBytes("v"), 0, 1}) == false {
		t.Fatalf("Error removing value 2nd time\n")
	}

	if table.Remove(&Item{3, GetBytes("v"), 1, 2}) == false {
		t.Fatalf("Error removing value 3rd time\n")
	}

	fmt.Printf("... done\n")
}

func TestLoadFactor(t *testing.T) {
	fmt.Printf("TestLoadFactor: ...\n")
	numBuckets := 1000
	var table *Table

	for depth := 1; depth < 32; depth *= 2 {
		count := -1
		ok := true
		table = NewTable("t", numBuckets, depth, 64, nil, int64(depth))
		for ok {
			count += 1
			val := GetBytes(strconv.Itoa(rand.Int()))
			ok, _ = table.Insert(&Item{rand.Int(), val, randBucket(numBuckets), randBucket(numBuckets)})
		}

		if table.GetNumElements() != count {
			t.Fatalf("Mismatch count=%v with GetNumElements()=%v \n", count, table.GetNumElements())
		}
		fmt.Printf("count=%v, depth=%v, capacity=%v, loadfactor=%v \n", count, depth, table.GetCapacity(), (float64(count) / float64(table.GetCapacity())))
	}

	fmt.Printf("... done\n")
}
<commit_msg>add test for bucket method<commit_after>package cuckoo

import (
	"bytes"
	"fmt"
	"math/rand"
	"strconv"
	"testing"
)

// Test identical state after operations
// Test contains after insert/remove sequence
// Test insert same value twice

func GetBytes(val string) []byte {
	buf := make([]byte, 64)
	copy(buf, bytes.NewBufferString(val).Bytes())
	return buf
}

func randBucket(numBuckets int) int {
	result := rand.Int() % numBuckets
	if result < 0 {
		result = result * -1
	}
	return result
}

func TestGetCapacity(t *testing.T) {
	fmt.Printf("TestGetCapacity: ...\n")

	table := NewTable("t", 10, 2, 64, nil, 0)
	if table.GetCapacity() != 20 {
		t.Fatalf("table returned wrong value for GetCapacity=%v. Expecting 20\n")
	}

	table = NewTable("t", 1, 1, 64, nil, 0)
	if table.GetCapacity() != 1 {
		t.Fatalf("table returned wrong value for GetCapacity=%v. Expecting 1\n")
	}

	table = NewTable("t", 0, 0, 64, nil, 0)
	if table.GetCapacity() != 0 {
		t.Fatalf("table returned wrong value for GetCapacity=%v. Expecting 0\n")
	}

	fmt.Printf("... done \n")
}

func TestBasic(t *testing.T) {
	table := NewTable("t", 10, 2, 64, nil, 0)

	fmt.Printf("TestBasic: check empty...\n")
	if table.GetNumElements() != 0 {
		t.Fatalf("empty table returned %v for GetNumElements()\n", table.GetNumElements())
	}

	fmt.Printf("TestBasic: Check contains non-existent value...\n")
	if table.Contains(&Item{0, GetBytes(""), 0, 1}) == true {
		t.Fatalf("empty table returned true for Contains()\n")
	}

	fmt.Printf("TestBasic: remove non-existent value ...\n")
	if table.Remove(&Item{1, GetBytes("value1"), 0, 1}) == true {
		t.Fatalf("empty table returned true for Remove()\n")
	}

	fmt.Printf("TestBasic: check empty...\n")
	if table.GetNumElements() != 0 {
		t.Fatalf("empty table returned %v for GetNumElements()\n", table.GetNumElements())
	}

	fmt.Printf("TestBasic: Insert value ...\n")
	ok, itm := table.Insert(&Item{1, GetBytes("value1"), 0, 1})
	if itm != nil || ok != true {
		t.Fatalf("error inserting into table (0, 1, value1)\n")
	}

	fmt.Printf("TestBasic: Check inserted value...\n")
	if table.Contains(&Item{1, GetBytes("value1"), 0, 1}) == false {
		t.Fatalf("cannot find recently inserted value\n")
	}

	fmt.Printf("TestBasic: Check inserted value w/o full reference...\n")
	if table.Contains(&Item{1, GetBytes(""), 0, 1}) == false {
		t.Fatalf("cannot find recently inserted value\n")
	}

	fmt.Printf("TestBasic: Check non-existent value...\n")
	if table.Contains(&Item{2, GetBytes("value2"), 0, 1}) == true {
		t.Fatalf("contains a non-existent value\n")
	}

	fmt.Printf("TestBasic: check 1 element...\n")
	if table.GetNumElements() != 1 {
		t.Fatalf("empty table returned %v for GetNumElements()\n", table.GetNumElements())
	}

	fmt.Printf("TestBasic: remove existing value ...\n")
	if table.Remove(&Item{1, GetBytes("value1"), 0, 1}) == false {
		t.Fatalf("error removing existing value (0, 1, value1)\n")
	}

	fmt.Printf("TestBasic: check 0 element...\n")
	if table.GetNumElements() != 0 {
		t.Fatalf("empty table returned %v for GetNumElements()\n", table.GetNumElements())
	}

	fmt.Printf("TestBasic: remove recently removed value ...\n")
	if table.Remove(&Item{1, GetBytes("value1"), 0, 1}) == true {
		t.Fatalf("empty table returned true for Remove()\n")
	}

	fmt.Printf("TestBasic: check 0 element...\n")
	if table.GetNumElements() != 0 {
		t.Fatalf("empty table returned %v for GetNumElements()\n", table.GetNumElements())
	}

	fmt.Printf("... done \n")
}

func TestBucket(t *testing.T) {
	table := NewTable("t", 10, 2, 64, nil, 0)
	item := &Item{1, GetBytes("value"), 5, 5}
	ok, _ := table.Insert(item)
	if !ok {
		t.Fatalf("Failed to insert item.")
	}
	if item.Bucket(table) != 5 {
		t.Fatalf("Bucket should report expected item position.")
	}
}

func TestOutOfBounds(t *testing.T) {
	table := NewTable("t", 10, 2, 64, nil, 0)

	fmt.Printf("TestOutOfBounds: Insert() out of bounds...\n")
	ok, itm := table.Insert(&Item{1, GetBytes("value1"), 100, 100})
	if ok == true {
		t.Fatalf("Insert returned true with out of bound buckets\n")
	}
	if itm != nil {
		t.Fatalf("Insert returned wrong values\n")
	}

	fmt.Printf("TestOutOfBounds: Contains() out of bounds...\n")
	if table.Contains(&Item{1, GetBytes("value1"), 100, 100}) == true {
		t.Fatalf("Contains returned true with out of bound buckets\n")
	}

	fmt.Printf("TestOutOfBounds: Remove() out of bounds...\n")
	if table.Remove(&Item{1, GetBytes("value1"), 100, 100}) == true {
		t.Fatalf("Remove() returned true with out of bound buckets\n")
	}

	fmt.Printf("... done \n")
}

func TestFullTable(t *testing.T) {
	numBuckets := 100
	depth := 4

	capacity := numBuckets * depth
	entries := make([]Item, 0, capacity)
	table := NewTable("t", numBuckets, depth, 64, nil, 0)
	ok := true
	count := 0
	var evic *Item
	var b1, b2 int

	// Insert random values until we've reached a limit
	fmt.Printf("TestFullTable: Insert until reach capacity...\n")
	for ok {
		b1 = randBucket(numBuckets)
		b2 = randBucket(numBuckets)
		id := rand.Int()
		val := GetBytes(strconv.Itoa(rand.Int()))
		entries = append(entries, Item{id, nil, b1, b2})
		ok, evic = table.Insert(&Item{id, val, b1, b2})

		if ok {
			count += 1
			if table.Contains(&Item{id, nil, b1, b2}) == false {
				t.Fatalf("Insert() succeeded, but Contains failed\n")
			}
			if count != table.GetNumElements() {
				t.Fatalf("Number of successful Inserts(), %v, does not match GetNumElements(), %v \n", count, table.GetNumElements())
			}
		}
	}

	// Middle count check
	fmt.Printf("TestFullTable: Fully Loaded check...\n")
	if count != table.GetNumElements() {
		t.Fatalf("Number of successful Inserts(), %v, does not match GetNumElements(), %v \n", count, table.GetNumElements())
	}
	maxCount := count

	// Remove elements one by one
	fmt.Printf("TestFullTable: Remove each element...\n")
	for _, entry := range entries {
		if !entry.Equals(evic) {
			ok = table.Remove(&entry)
			if ok == false {
				t.Fatalf("Cannot Remove() an element believed to be in the table. item %d of %d", count, maxCount)
			} else {
				count -= 1
				if count != table.GetNumElements() {
					t.Fatalf("GetNumElements()=%v returned a value that didn't match what was expected=%v \n", table.GetNumElements(), count)
				}
			}
		}
	}

	// Final count check
	fmt.Printf("TestFullTable: Final count check...\n")
	if table.GetNumElements() != 0 {
		t.Fatalf("GetNumElements() returns %v when table should be empty \n", table.GetNumElements())
	}

	fmt.Printf("... done\n")
}

func TestDuplicateValues(t *testing.T) {
	fmt.Printf("TestDuplicateValues: ...\n")
	table := NewTable("t", 10, 2, 64, nil, 0)

	ok, itm := table.Insert(&Item{1, GetBytes("v"), 0, 1})
	if itm != nil || ok == false {
		t.Fatalf("Error inserting value \n")
	}

	ok, itm = table.Insert(&Item{2, GetBytes("v"), 0, 1})
	if itm != nil || ok == false {
		t.Fatalf("Error inserting value again \n")
	}

	ok, itm = table.Insert(&Item{3, GetBytes("v"), 1, 2})
	if itm != nil || ok == false {
		t.Fatalf("Error inserting value in shifted buckets\n")
	}

	if table.Remove(&Item{1, GetBytes("v"), 0, 1}) == false {
		t.Fatalf("Error removing value 1st time\n")
	}

	if table.Remove(&Item{2, GetBytes("v"), 0, 1}) == false {
		t.Fatalf("Error removing value 2nd time\n")
	}

	if table.Remove(&Item{3, GetBytes("v"), 1, 2}) == false {
		t.Fatalf("Error removing value 3rd time\n")
	}

	fmt.Printf("... done\n")
}

func TestLoadFactor(t *testing.T) {
	fmt.Printf("TestLoadFactor: ...\n")
	numBuckets := 1000
	var table *Table

	for depth := 1; depth < 32; depth *= 2 {
		count := -1
		ok := true
		table = NewTable("t", numBuckets, depth, 64, nil, int64(depth))
		for ok {
			count += 1
			val := GetBytes(strconv.Itoa(rand.Int()))
			ok, _ = table.Insert(&Item{rand.Int(), val, randBucket(numBuckets), randBucket(numBuckets)})
		}

		if table.GetNumElements() != count {
			t.Fatalf("Mismatch count=%v with GetNumElements()=%v \n", count, table.GetNumElements())
		}
		fmt.Printf("count=%v, depth=%v, capacity=%v, loadfactor=%v \n", count, depth, table.GetCapacity(), (float64(count) / float64(table.GetCapacity())))
	}

	fmt.Printf("... done\n")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/Netflix-Skunkworks/go-jira/jira/cli"
	"github.com/docopt/docopt-go"
	"github.com/op/go-logging"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"strings"
)

var log = logging.MustGetLogger("jira")
var format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"

func main() {
	user := os.Getenv("USER")
	home := os.Getenv("HOME")
	usage := fmt.Sprintf(`
Usage:
  jira [-v ...] [-u USER] [-e URI] [-t FILE] (ls|list) ( [-q JQL] | [-p PROJECT] [-c COMPONENT] [-a ASSIGNEE] [-i ISSUETYPE] [-w WATCHER] [-r REPORTER])
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] view ISSUE
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] edit ISSUE [--noedit] [-m COMMENT] [-o KEY=VAL]...
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] create [--noedit] [-p PROJECT] [-i ISSUETYPE] [-o KEY=VAL]...
  jira [-v ...] [-u USER] [-e URI] [-b] DUPLICATE dups ISSUE
  jira [-v ...] [-u USER] [-e URI] [-b] BLOCKER blocks ISSUE
  jira [-v ...] [-u USER] [-e URI] [-b] watch ISSUE [-w WATCHER]
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] (trans|transition) TRANSITION ISSUE [-m COMMENT] [-o KEY=VAL] [--noedit]
  jira [-v ...] [-u USER] [-e URI] [-b] ack ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira [-v ...] [-u USER] [-e URI] [-b] close ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira [-v ...] [-u USER] [-e URI] [-b] resolve ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira [-v ...] [-u USER] [-e URI] [-b] reopen ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira [-v ...] [-u USER] [-e URI] [-b] start ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira [-v ...] [-u USER] [-e URI] [-b] stop ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] comment ISSUE [-m COMMENT]
  jira [-v ...] [-u USER] [-e URI] [-b] take ISSUE
  jira [-v ...] [-u USER] [-e URI] [-b] (assign|give) ISSUE ASSIGNEE
  jira [-v ...] [-u USER] [-e URI] [-t FILE] fields
  jira [-v ...] [-u USER] [-e URI] [-t FILE] issuelinktypes
  jira [-v ...] [-u USER] [-e URI] [-b][-t FILE] transmeta ISSUE
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] editmeta ISSUE
  jira [-v ...] [-u USER] [-e URI] [-t FILE] issuetypes [-p PROJECT]
  jira [-v ...] [-u USER] [-e URI] [-t FILE] createmeta [-p PROJECT] [-i ISSUETYPE]
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] transitions ISSUE
  jira [-v ...] export-templates [-d DIR]
  jira [-v ...] [-u USER] [-e URI] (b|browse) ISSUE
  jira [-v ...] [-u USER] [-e URI] [-t FILE] login
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] ISSUE

General Options:
  -e --endpoint=URI   URI to use for jira
  -h --help           Show this usage
  -t --template=FILE  Template file to use for output/editing
  -u --user=USER      Username to use for authenticaion (default: %s)
  -v --verbose        Increase output logging
  --version           Show this version

Command Options:
  -a --assignee=USER        Username assigned the issue
  -b --browse               Open your browser to the Jira issue
  -c --component=COMPONENT  Component to Search for
  -d --directory=DIR        Directory to export templates to (default: %s)
  -f --queryfields          Fields that are used in "list" template: (default: summary)
  -i --issuetype=ISSUETYPE  Jira Issue Type (default: Bug)
  -m --comment=COMMENT      Comment message for transition
  -o --override=KEY:VAL     Set custom key/value pairs
  -p --project=PROJECT      Project to Search for
  -q --query=JQL            Jira Query Language expression for the search
  -r --reporter=USER        Reporter to search for
  -w --watcher=USER         Watcher to add to issue (default: %s)
                            or Watcher to search for
`, user, fmt.Sprintf("%s/.jira.d/templates", home), user)

	args, err := docopt.Parse(usage, nil, true, "0.0.1", false, false)
	if err != nil {
		log.Error("Failed to parse options: %s", err)
		os.Exit(1)
	}
	logBackend := logging.NewLogBackend(os.Stderr, "", 0)
	logging.SetBackend(
		logging.NewBackendFormatter(
			logBackend,
			logging.MustStringFormatter(format),
		),
	)
	logging.SetLevel(logging.NOTICE, "")
	if verbose, ok := args["--verbose"]; ok {
		if verbose.(int) > 1 {
			logging.SetLevel(logging.DEBUG, "")
		} else if verbose.(int) > 0 {
			logging.SetLevel(logging.INFO, "")
		}
	}

	log.Info("Args: %v", args)

	opts := make(map[string]string)
	loadConfigs(opts)

	// strip the "--" off the command line options
	// and populate the opts that we pass to the cli ctor
	for key, val := range args {
		if val != nil && strings.HasPrefix(key, "--") {
			opt := key[2:]
			if opt == "override" {
				for _, v := range val.([]string) {
					if strings.Contains(v, "=") {
						kv := strings.SplitN(v, "=", 2)
						opts[kv[0]] = kv[1]
					} else {
						log.Error("Malformed override, expected KEY=VALUE, got %s", v)
						os.Exit(1)
					}
				}
			} else {
				switch v := val.(type) {
				case string:
					opts[opt] = v
				case int:
					opts[opt] = fmt.Sprintf("%d", v)
				case bool:
					opts[opt] = fmt.Sprintf("%t", v)
				}
			}
		}
	}

	// cant use proper [default:x] syntax in docopt
	// because only want to default if the option is not
	// already specified in some .jira.d/config.yml file
	if _, ok := opts["user"]; !ok {
		opts["user"] = user
	}
	if _, ok := opts["queryfields"]; !ok {
		opts["queryfields"] = "summary"
	}
	if _, ok := opts["directory"]; !ok {
		opts["directory"] = fmt.Sprintf("%s/.jira.d/templates", home)
	}

	if _, ok := opts["endpoint"]; !ok {
		log.Error("endpoint option required.  Either use --endpoint or set a enpoint option in your ~/.jira.d/config.yml file")
		os.Exit(1)
	}

	c := cli.New(opts)

	log.Debug("opts: %s", opts)

	validCommand := func(cmd string) bool {
		if val, ok := args[cmd]; ok && val.(bool) {
			return true
		}
		return false
	}

	validOpt := func(opt string, dflt interface{}) interface{} {
		if val, ok := opts[opt]; ok {
			return val
		}
		if dflt == nil {
			log.Error("Missing required option --%s or \"%s\" property override in the config file", opt, opt)
			os.Exit(1)
		}
		return dflt
	}

	setEditing := func(dflt bool) {
		if dflt {
			if val, ok := opts["noedit"]; ok && val == "true" {
				opts["edit"] = "false"
			} else {
				opts["edit"] = "true"
			}
		} else {
			if val, ok := opts["edit"]; ok && val == "true" {
				opts["edit"] = "true"
			} else {
				opts["edit"] = "false"
			}
		}
	}

	if validCommand("login") {
		err = c.CmdLogin()
	} else if validCommand("fields") {
		err = c.CmdFields()
	} else if validCommand("ls") || validCommand("list") {
		err = c.CmdList()
	} else if validCommand("edit") {
		setEditing(true)
		err = c.CmdEdit(args["ISSUE"].(string))
	} else if validCommand("editmeta") {
		err = c.CmdEditMeta(args["ISSUE"].(string))
	} else if validCommand("transmeta") {
		err = c.CmdTransitionMeta(args["ISSUE"].(string))
	} else if validCommand("issuelinktypes") {
		err = c.CmdIssueLinkTypes()
	} else if validCommand("issuetypes") {
		err = c.CmdIssueTypes(validOpt("project", nil).(string))
	} else if validCommand("createmeta") {
		err = c.CmdCreateMeta(
			validOpt("project", nil).(string),
			validOpt("issuetype", "Bug").(string),
		)
	} else if validCommand("create") {
		setEditing(true)
		err = c.CmdCreate(
			validOpt("project", nil).(string),
			validOpt("issuetype", "Bug").(string),
		)
	} else if validCommand("transitions") {
		err = c.CmdTransitions(args["ISSUE"].(string))
	} else if validCommand("blocks") {
		err = c.CmdBlocks(
			args["BLOCKER"].(string),
			args["ISSUE"].(string),
		)
	} else if validCommand("dups") {
		err = c.CmdDups(
			args["DUPLICATE"].(string),
			args["ISSUE"].(string),
		)
	} else if validCommand("watch") {
		err = c.CmdWatch(
			args["ISSUE"].(string),
			validOpt("watcher", user).(string),
		)
	} else if validCommand("trans") || validCommand("transition") {
		setEditing(true)
		err = c.CmdTransition(
			args["ISSUE"].(string),
			args["TRANSITION"].(string),
		)
	} else if validCommand("close") {
		setEditing(false)
		err = c.CmdTransition(args["ISSUE"].(string), "close")
	} else if validCommand("ack") {
		setEditing(false)
		err = c.CmdTransition(args["ISSUE"].(string), "acknowledge")
	} else if validCommand("reopen") {
		setEditing(false)
		err = c.CmdTransition(args["ISSUE"].(string), "reopen")
	} else if validCommand("resolve") {
		setEditing(false)
		err = c.CmdTransition(args["ISSUE"].(string), "resolve")
	} else if validCommand("start") {
		setEditing(false)
		err = c.CmdTransition(args["ISSUE"].(string), "start")
	} else if validCommand("stop") {
		setEditing(false)
		err = c.CmdTransition(args["ISSUE"].(string), "stop")
	} else if validCommand("comment") {
		setEditing(true)
		err = c.CmdComment(args["ISSUE"].(string))
	} else if validCommand("take") {
		err = c.CmdAssign(args["ISSUE"].(string), user)
	} else if validCommand("browse") || validCommand("b") {
		opts["browse"] = "true"
		err = c.Browse(args["ISSUE"].(string))
	} else if validCommand("export-templates") {
		err = c.CmdExportTemplates()
	} else if validCommand("assign") || validCommand("give") {
		err = c.CmdAssign(
			args["ISSUE"].(string),
			args["ASSIGNEE"].(string),
		)
	} else if val, ok := args["ISSUE"]; ok {
		err = c.CmdView(val.(string))
	}

	if err != nil {
		os.Exit(1)
	}
	os.Exit(0)
}

func parseYaml(file string, opts map[string]string) {
	if fh, err := ioutil.ReadFile(file); err == nil {
		log.Debug("Found Config file: %s", file)
		yaml.Unmarshal(fh, &opts)
	}
}

func loadConfigs(opts map[string]string) {
	paths := cli.FindParentPaths(".jira.d/config.yml")
	// prepend
	paths = append([]string{"/etc/jira-cli.yml"}, paths...)

	for _, file := range paths {
		parseYaml(file, opts)
	}
}
<commit_msg>bump version<commit_after>package main

import (
	"fmt"
	"github.com/Netflix-Skunkworks/go-jira/jira/cli"
	"github.com/docopt/docopt-go"
	"github.com/op/go-logging"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"strings"
)

var log = logging.MustGetLogger("jira")
var format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"

func main() {
	user := os.Getenv("USER")
	home := os.Getenv("HOME")
	usage := fmt.Sprintf(`
Usage:
  jira [-v ...] [-u USER] [-e URI] [-t FILE] (ls|list) ( [-q JQL] | [-p PROJECT] [-c COMPONENT] [-a ASSIGNEE] [-i ISSUETYPE] [-w WATCHER] [-r REPORTER])
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] view ISSUE
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] edit ISSUE [--noedit] [-m COMMENT] [-o KEY=VAL]...
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] create [--noedit] [-p PROJECT] [-i ISSUETYPE] [-o KEY=VAL]...
  jira [-v ...] [-u USER] [-e URI] [-b] DUPLICATE dups ISSUE
  jira [-v ...] [-u USER] [-e URI] [-b] BLOCKER blocks ISSUE
  jira [-v ...] [-u USER] [-e URI] [-b] watch ISSUE [-w WATCHER]
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] (trans|transition) TRANSITION ISSUE [-m COMMENT] [-o KEY=VAL] [--noedit]
  jira [-v ...] [-u USER] [-e URI] [-b] ack ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira [-v ...] [-u USER] [-e URI] [-b] close ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira [-v ...] [-u USER] [-e URI] [-b] resolve ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira [-v ...] [-u USER] [-e URI] [-b] reopen ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira [-v ...] [-u USER] [-e URI] [-b] start ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira [-v ...] [-u USER] [-e URI] [-b] stop ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] comment ISSUE [-m COMMENT]
  jira [-v ...] [-u USER] [-e URI] [-b] take ISSUE
  jira [-v ...] [-u USER] [-e URI] [-b] (assign|give) ISSUE ASSIGNEE
  jira [-v ...] [-u USER] [-e URI] [-t FILE] fields
  jira [-v ...] [-u USER] [-e URI] [-t FILE] issuelinktypes
  jira [-v ...] [-u USER] [-e URI] [-b][-t FILE] transmeta ISSUE
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] editmeta ISSUE
  jira [-v ...] [-u USER] [-e URI] [-t FILE] issuetypes [-p PROJECT]
  jira [-v ...] [-u USER] [-e URI] [-t FILE] createmeta [-p PROJECT] [-i ISSUETYPE]
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] transitions ISSUE
  jira [-v ...] export-templates [-d DIR]
  jira [-v ...] [-u USER] [-e URI] (b|browse) ISSUE
  jira [-v ...] [-u USER] [-e URI] [-t FILE] login
  jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] ISSUE

General Options:
  -e --endpoint=URI   URI to use for jira
  -h --help           Show this usage
  -t --template=FILE  Template file to use for output/editing
  -u --user=USER      Username to use for authenticaion (default: %s)
  -v --verbose        Increase output logging
  --version           Show this version

Command Options:
  -a --assignee=USER        Username assigned the issue
  -b --browse               Open your browser to the Jira issue
  -c --component=COMPONENT  Component to Search for
  -d --directory=DIR        Directory to export templates to (default: %s)
  -f --queryfields          Fields that are used in "list" template: (default: summary)
  -i --issuetype=ISSUETYPE  Jira Issue Type (default: Bug)
  -m --comment=COMMENT      Comment message for transition
  -o --override=KEY:VAL     Set custom key/value pairs
  -p --project=PROJECT      Project to Search for
  -q --query=JQL            Jira Query Language expression for the search
  -r --reporter=USER        Reporter to search for
  -w --watcher=USER         Watcher to add to issue (default: %s)
                            or Watcher to search for
`, user, fmt.Sprintf("%s/.jira.d/templates", home), user)

	args, err := docopt.Parse(usage, nil, true, "0.0.2", false, false)
	if err != nil {
		log.Error("Failed to parse options: %s", err)
		os.Exit(1)
	}
	logBackend := logging.NewLogBackend(os.Stderr, "", 0)
	logging.SetBackend(
		logging.NewBackendFormatter(
			logBackend,
			logging.MustStringFormatter(format),
		),
	)
	logging.SetLevel(logging.NOTICE, "")
	if verbose, ok := args["--verbose"]; ok {
		if verbose.(int) > 1 {
			logging.SetLevel(logging.DEBUG, "")
		} else if verbose.(int) > 0 {
			logging.SetLevel(logging.INFO, "")
		}
	}

	log.Info("Args: %v", args)

	opts := make(map[string]string)
	loadConfigs(opts)

	// strip the "--" off the command line options
	// and populate the opts that we pass to the cli ctor
	for key, val := range args {
		if val != nil && strings.HasPrefix(key, "--") {
			opt := key[2:]
			if opt == "override" {
				for _, v := range val.([]string) {
					if strings.Contains(v, "=") {
						kv := strings.SplitN(v, "=", 2)
						opts[kv[0]] = kv[1]
					} else {
						log.Error("Malformed override, expected KEY=VALUE, got %s", v)
						os.Exit(1)
					}
				}
			} else {
				switch v := val.(type) {
				case string:
					opts[opt] = v
				case int:
					opts[opt] = fmt.Sprintf("%d", v)
				case bool:
					opts[opt] = fmt.Sprintf("%t", v)
				}
			}
		}
	}

	// cant use proper [default:x] syntax in docopt
	// because only want to default if the option is not
	// already specified in some .jira.d/config.yml file
	if _, ok := opts["user"]; !ok {
		opts["user"] = user
	}
	if _, ok := opts["queryfields"]; !ok {
		opts["queryfields"] = "summary"
	}
	if _, ok := opts["directory"]; !ok {
		opts["directory"] = fmt.Sprintf("%s/.jira.d/templates", home)
	}

	if _, ok := opts["endpoint"]; !ok {
		log.Error("endpoint option required.  Either use --endpoint or set a enpoint option in your ~/.jira.d/config.yml file")
		os.Exit(1)
	}

	c := cli.New(opts)

	log.Debug("opts: %s", opts)

	validCommand := func(cmd string) bool {
		if val, ok := args[cmd]; ok && val.(bool) {
			return true
		}
		return false
	}

	validOpt := func(opt string, dflt interface{}) interface{} {
		if val, ok := opts[opt]; ok {
			return val
		}
		if dflt == nil {
			log.Error("Missing required option --%s or \"%s\" property override in the config file", opt, opt)
			os.Exit(1)
		}
		return dflt
	}

	setEditing := func(dflt bool) {
		if dflt {
			if val, ok := opts["noedit"]; ok && val == "true" {
				opts["edit"] = "false"
			} else {
				opts["edit"] = "true"
			}
		} else {
			if val, ok := opts["edit"]; ok && val == "true" {
				opts["edit"] = "true"
			} else {
				opts["edit"] = "false"
			}
		}
	}

	if validCommand("login") {
		err = c.CmdLogin()
	} else if validCommand("fields") {
		err = c.CmdFields()
	} else if validCommand("ls") || validCommand("list") {
		err = c.CmdList()
	} else if validCommand("edit") {
		setEditing(true)
		err = c.CmdEdit(args["ISSUE"].(string))
	} else if validCommand("editmeta") {
		err = c.CmdEditMeta(args["ISSUE"].(string))
	} else if validCommand("transmeta") {
		err = c.CmdTransitionMeta(args["ISSUE"].(string))
	} else if validCommand("issuelinktypes") {
		err = c.CmdIssueLinkTypes()
	} else if validCommand("issuetypes") {
		err = c.CmdIssueTypes(validOpt("project", nil).(string))
	} else if validCommand("createmeta") {
		err = c.CmdCreateMeta(
			validOpt("project", nil).(string),
			validOpt("issuetype", "Bug").(string),
		)
	} else if validCommand("create") {
		setEditing(true)
		err = c.CmdCreate(
			validOpt("project", nil).(string),
			validOpt("issuetype", "Bug").(string),
		)
	} else if validCommand("transitions") {
		err = c.CmdTransitions(args["ISSUE"].(string))
	} else if validCommand("blocks") {
		err = c.CmdBlocks(
			args["BLOCKER"].(string),
			args["ISSUE"].(string),
		)
	} else if validCommand("dups") {
		err = c.CmdDups(
			args["DUPLICATE"].(string),
			args["ISSUE"].(string),
		)
	} else if validCommand("watch") {
		err = c.CmdWatch(
			args["ISSUE"].(string),
			validOpt("watcher", user).(string),
		)
	} else if validCommand("trans") || validCommand("transition") {
		setEditing(true)
		err = c.CmdTransition(
			args["ISSUE"].(string),
			args["TRANSITION"].(string),
		)
	} else if validCommand("close") {
		setEditing(false)
		err = c.CmdTransition(args["ISSUE"].(string), "close")
	} else if validCommand("ack") {
		setEditing(false)
		err = c.CmdTransition(args["ISSUE"].(string), "acknowledge")
	} else if validCommand("reopen") {
		setEditing(false)
		err = c.CmdTransition(args["ISSUE"].(string), "reopen")
	} else if validCommand("resolve") {
		setEditing(false)
		err = c.CmdTransition(args["ISSUE"].(string), "resolve")
	} else if validCommand("start") {
		setEditing(false)
		err = c.CmdTransition(args["ISSUE"].(string), "start")
	} else if validCommand("stop") {
		setEditing(false)
		err = c.CmdTransition(args["ISSUE"].(string), "stop")
	} else if validCommand("comment") {
		setEditing(true)
		err = c.CmdComment(args["ISSUE"].(string))
	} else if validCommand("take") {
		err = c.CmdAssign(args["ISSUE"].(string), user)
	} else if validCommand("browse") || validCommand("b") {
		opts["browse"] = "true"
		err = c.Browse(args["ISSUE"].(string))
	} else if validCommand("export-templates") {
		err = c.CmdExportTemplates()
	} else if validCommand("assign") || validCommand("give") {
		err = c.CmdAssign(
			args["ISSUE"].(string),
			args["ASSIGNEE"].(string),
		)
	} else if val, ok := args["ISSUE"]; ok {
		err = c.CmdView(val.(string))
	}

	if err != nil {
		os.Exit(1)
	}
	os.Exit(0)
}

func parseYaml(file string, opts map[string]string) {
	if fh, err := ioutil.ReadFile(file); err == nil {
		log.Debug("Found Config file: %s", file)
		yaml.Unmarshal(fh, &opts)
	}
}

func loadConfigs(opts map[string]string) {
	paths := cli.FindParentPaths(".jira.d/config.yml")
	// prepend
	paths = append([]string{"/etc/jira-cli.yml"}, paths...)

	for _, file := range paths {
		parseYaml(file, opts)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/tueftler/doget/command"
	"github.com/tueftler/doget/command/build"
	"github.com/tueftler/doget/command/clean"
	"github.com/tueftler/doget/command/dump"
	"github.com/tueftler/doget/command/transform"
	"github.com/tueftler/doget/config"
	"github.com/tueftler/doget/docker"
	"github.com/tueftler/doget/dockerfile"
	"github.com/tueftler/doget/provides"
	"github.com/tueftler/doget/use"
)

var (
	commands = make(map[string]command.Command)
	version  = "1.0.3"
)

func init() {
	commands["dump"] = dump.NewCommand("dump")
	commands["transform"] = transform.NewCommand("transform")
	commands["clean"] = clean.NewCommand("clean")
	commands["build"] = build.NewCommand(
		"build",
		commands["transform"],
		commands["clean"],
		docker.Create("docker"),
	)
}

func configuration(file string) (*config.Configuration, error) {
	if file == "" {
		return config.Default().Merge(config.SearchPath()...)
	} else {
		return config.Empty().MustMerge(file)
	}
}

func main() {
	var (
		cmdName    = flag.String("#1", "", "Command, one of [build, clean, dump, transform]")
		configFile = flag.String("config", "", "Configuration file to use")
	)
	flag.Parse()

	configuration, err := configuration(*configFile)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}

	*cmdName = flag.Arg(0)
	if delegate, ok := commands[*cmdName]; ok {
		parser := dockerfile.NewParser().
			Extend("USE", use.New(configuration.Repositories).Extension).
			Extend("PROVIDES", provides.Extension)

		args := flag.Args()
		if err := delegate.Run(parser, args[1:len(args)]); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	} else {
		fmt.Printf("Cannot handle command %q\n", *cmdName)
		fmt.Printf("DoGet version %s, usage:\n", version)
		flag.PrintDefaults()
		os.Exit(2)
	}
}
<commit_msg>Bump version after release [skip ci]<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/tueftler/doget/command"
	"github.com/tueftler/doget/command/build"
	"github.com/tueftler/doget/command/clean"
	"github.com/tueftler/doget/command/dump"
	"github.com/tueftler/doget/command/transform"
	"github.com/tueftler/doget/config"
	"github.com/tueftler/doget/docker"
	"github.com/tueftler/doget/dockerfile"
	"github.com/tueftler/doget/provides"
	"github.com/tueftler/doget/use"
)

var (
	commands = make(map[string]command.Command)
	version  = "1.0.4-dev"
)

func init() {
	commands["dump"] = dump.NewCommand("dump")
	commands["transform"] = transform.NewCommand("transform")
	commands["clean"] = clean.NewCommand("clean")
	commands["build"] = build.NewCommand(
		"build",
		commands["transform"],
		commands["clean"],
		docker.Create("docker"),
	)
}

func configuration(file string) (*config.Configuration, error) {
	if file == "" {
		return config.Default().Merge(config.SearchPath()...)
	} else {
		return config.Empty().MustMerge(file)
	}
}

func main() {
	var (
		cmdName    = flag.String("#1", "", "Command, one of [build, clean, dump, transform]")
		configFile = flag.String("config", "", "Configuration file to use")
	)
	flag.Parse()

	configuration, err := configuration(*configFile)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}

	*cmdName = flag.Arg(0)
	if delegate, ok := commands[*cmdName]; ok {
		parser := dockerfile.NewParser().
			Extend("USE", use.New(configuration.Repositories).Extension).
			Extend("PROVIDES", provides.Extension)

		args := flag.Args()
		if err := delegate.Run(parser, args[1:len(args)]); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	} else {
		fmt.Printf("Cannot handle command %q\n", *cmdName)
		fmt.Printf("DoGet version %s, usage:\n", version)
		flag.PrintDefaults()
		os.Exit(2)
	}
}
<|endoftext|>