<commit_before>package main

import (
	"fmt"
	text "github.com/tonnerre/golang-text"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

func getPath(p string) string {
	path, _ := filepath.Abs(p)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		log.Fatal(err)
	}
	return path
}

func asKey(p string) string {
	basename := filepath.Base(p)
	return strings.TrimSuffix(basename, filepath.Ext(basename))
}

// Info is the main storage for information. All yaml files map to this.
type Info struct {
	ID      string
	Type    string `yaml:"type"`
	Summary string `yaml:"summary"`
	Body    string `yaml:"body"`
}

// LoadInfo loads an Info object from a file path
func LoadInfo(p string) (i Info, err error) {
	data, err := ioutil.ReadFile(p)
	if err != nil {
		log.Fatal("Reading file failed: ", p)
	}

	i = Info{ID: asKey(p)}
	yaml.Unmarshal(data, &i)
	return
}

func (i Info) String() string {
	return fmt.Sprintf("I: %s", i.ID)
}

func (i *Info) Execute() {
	if i.Type == "info" {
		i.PrintBody()
	}
}

// PrintBody will pretty format the body of the item
func (i *Info) PrintBody() {
	out := text.Wrap(i.Body, 80)
	fmt.Println(out)
}
<commit_msg>Add `command` type, with execution! |o/<commit_after>package main

import (
	"fmt"
	text "github.com/tonnerre/golang-text"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func getPath(p string) string {
	path, _ := filepath.Abs(p)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		log.Fatal(err)
	}
	return path
}

func asKey(p string) string {
	basename := filepath.Base(p)
	return strings.TrimSuffix(basename, filepath.Ext(basename))
}

// LoadInfo loads an Info object from a file path
func LoadInfo(p string) (i Info, err error) {
	data, err := ioutil.ReadFile(p)
	if err != nil {
		log.Fatal("Reading file failed: ", p)
	}

	i = Info{ID: asKey(p)}
	yaml.Unmarshal(data, &i)
	return
}

// Info is the main storage for information. All yaml files map to this.
type Info struct {
	ID      string
	Type    string `yaml:"type"`
	Summary string `yaml:"summary"`
	Body    string `yaml:"body"`
	Host    string `yaml:"host"`
	Command string `yaml:"command"`
}

func (i Info) String() string {
	return fmt.Sprintf("I: %s", i.ID)
}

// Execute will figure out the type of the info and execute accordingly
func (i *Info) Execute() {
	if i.Type == "info" {
		i.PrintBody()
	} else if i.Type == "command" {
		i.ExecuteCommand()
	}
}

// PrintBody will pretty format the body of the item
func (i *Info) PrintBody() {
	out := text.Wrap(i.Body, 80)
	fmt.Println(out)
}

// ExecuteCommand will execute the command specified by the item
func (i *Info) ExecuteCommand() {
	sh, _ := exec.LookPath("sh")
	args := []string{sh, "-c", i.Command}

	cmd := exec.Cmd{
		Path:   sh,
		Args:   args,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
	}

	err := cmd.Run()
	if err != nil {
		log.Fatal("oh noes :(")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

type info struct {
	os.FileInfo
	path  string
	depth int
	err   error
	infos []*info
}

//Think/Write pusodue code
//what is the right way to write this function...
func (inf *info) visit(opts *options) (dirs, files int) {
	fi, err := os.Stat(inf.path)
	if err != nil {
		inf.err = err
		return
	}
	inf.FileInfo = fi
	if !fi.IsDir() {
		return 0, 1
	}
	dir, err := os.Open(inf.path)
	if err != nil {
		inf.err = err
		return
	}
	names, err := dir.Readdirnames(-1)
	dir.Close()
	if err != nil {
		inf.err = err
		return
	}
	inf.infos = make([]*info, 0)
	for _, name := range names {
		// "all" option
		if !opts.all && strings.HasPrefix(name, ".") {
			continue
		}
		_inf := &info{
			path:  filepath.Join(inf.path, name),
			depth: inf.depth + 1,
		}
		d, f := _inf.visit(opts)
		// "dirs only" option
		if opts.dirsOnly && !_inf.IsDir() {
			continue
		}
		inf.infos = append(inf.infos, _inf)
		dirs, files = dirs+d, files+f
	}
	return dirs + 1, files
}

func (inf *info) print(indent string, opts *options) {
	if inf.err != nil {
		err := strings.Split(inf.err.Error(), ": ")[1]
		fmt.Printf("%s [%s]\n", inf.path, err)
		return
	}
	// Print name/path
	if inf.depth == 0 || opts.fullPath {
		fmt.Println(inf.path)
	} else {
		fmt.Println(inf.Name())
	}
	add := "│   "
	for i, _inf := range inf.infos {
		if i == len(inf.infos)-1 {
			fmt.Printf(indent + "└── ")
			add = "    "
		} else {
			fmt.Printf(indent + "├── ")
		}
		_inf.print(indent+add, opts)
	}
}
<commit_msg>feat(tree): add file size<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

type info struct {
	os.FileInfo
	path  string
	depth int
	err   error
	infos []*info
}

//Think/Write pusodue code
//what is the right way to write this function...
func (inf *info) visit(opts *options) (dirs, files int) {
	fi, err := os.Stat(inf.path)
	if err != nil {
		inf.err = err
		return
	}
	inf.FileInfo = fi
	if !fi.IsDir() {
		return 0, 1
	}
	dir, err := os.Open(inf.path)
	if err != nil {
		inf.err = err
		return
	}
	names, err := dir.Readdirnames(-1)
	dir.Close()
	if err != nil {
		inf.err = err
		return
	}
	inf.infos = make([]*info, 0)
	for _, name := range names {
		// "all" option
		if !opts.all && strings.HasPrefix(name, ".") {
			continue
		}
		_inf := &info{
			path:  filepath.Join(inf.path, name),
			depth: inf.depth + 1,
		}
		d, f := _inf.visit(opts)
		// "dirs only" option
		if opts.dirsOnly && !_inf.IsDir() {
			continue
		}
		inf.infos = append(inf.infos, _inf)
		dirs, files = dirs+d, files+f
	}
	return dirs + 1, files
}

func (inf *info) print(indent string, opts *options) {
	if inf.err != nil {
		err := strings.Split(inf.err.Error(), ": ")[1]
		fmt.Printf("%s [%s]\n", inf.path, err)
		return
	}
	var name, size string
	// name/path
	if inf.depth == 0 || opts.fullPath {
		name = inf.path
	} else {
		name = inf.Name()
	}
	if !inf.IsDir() {
		if opts.byteSize {
			size = fmt.Sprintf("%d", inf.Size())
			if pad := 11 - len(size); pad > 0 {
				size = strings.Repeat(" ", pad) + size
			}
		}
		if opts.unitSize {
			size = formatBytes(inf.Size())
			if pad := 4 - len(size); pad > 0 {
				size = strings.Repeat(" ", pad) + size
			}
		}
		// Print properties
		if props := size; len(props) > 0 {
			fmt.Printf("[%s]  ", props)
		}
	}
	// Print file details
	fmt.Println(name)
	add := "│   "
	for i, _inf := range inf.infos {
		if i == len(inf.infos)-1 {
			fmt.Printf(indent + "└── ")
			add = "    "
		} else {
			fmt.Printf(indent + "├── ")
		}
		_inf.print(indent+add, opts)
	}
}

// Convert bytes to human readable string. Like a 2 MB, 64.2 KB, 52 B
func formatBytes(i int64) (result string) {
	var n float64
	sFmt, eFmt := "%.01f", ""
	switch {
	case i > (1024 * 1024 * 1024):
		eFmt = "G"
		n = float64(i) / 1024 / 1024 / 1024
	case i > (1024 * 1024):
		eFmt = "M"
		n = float64(i) / 1024 / 1024
	case i > 1024:
		eFmt = "K"
		n = float64(i) / 1024
	default:
		sFmt = "%.0f"
		n = float64(i)
	}
	if eFmt != "" && n >= 10 {
		sFmt = "%.0f"
	}
	result = fmt.Sprintf(sFmt+eFmt, n)
	result = strings.Trim(result, " ")
	return
}
<|endoftext|>


<commit_before>package go_macro

import (
	_ "github.com/WindomZ/go-macro/bitcoin"
	_ "github.com/WindomZ/go-macro/captcha"
	_ "github.com/WindomZ/go-macro/def"
	_ "github.com/WindomZ/go-macro/http"
	_ "github.com/WindomZ/go-macro/macro"
	_ "github.com/WindomZ/go-macro/pinyin"
	_ "github.com/WindomZ/go-macro/time"
	_ "github.com/WindomZ/go-macro/uuid"
)
<commit_msg>fix: init<commit_after>package go_macro

import (
	_ "github.com/WindomZ/go-macro/bitcoin"
	_ "github.com/WindomZ/go-macro/captcha"
	_ "github.com/WindomZ/go-macro/def"
	_ "github.com/WindomZ/go-macro/http"
	_ "github.com/WindomZ/go-macro/macro"
	_ "github.com/WindomZ/go-macro/pinyin"
	_ "github.com/WindomZ/go-macro/store"
	_ "github.com/WindomZ/go-macro/time"
	_ "github.com/WindomZ/go-macro/uuid"
)
<|endoftext|>


<commit_before><commit_msg>Fixed two tests.<commit_after><|endoftext|>


<commit_before>package checkerlution

import (
	"encoding/json"
	"github.com/couchbaselabs/logg"
	cbot "github.com/tleyden/checkers-bot"
	ng "github.com/tleyden/neurgo"
	"os"
)

const (
	RUNNING_MODE = iota
	TRAINING_MODE
)

type Checkerlution struct {
	ourTeamId            cbot.TeamType
	cortex               *ng.Cortex
	currentGameState     GameStateVector
	currentPossibleMove  ValidMoveCortexInput
	latestActuatorOutput []float64
	mode                 int
	latestFitnessScore   float64
}

func (c *Checkerlution) Start(ourTeamId cbot.TeamType) {
	c.ourTeamId = ourTeamId
	c.CreateNeurgoCortex()
	cortex := c.cortex
	cortex.Run()

}

func (c *Checkerlution) StartWithCortex(cortex *ng.Cortex, ourTeamId cbot.TeamType) {
	c.ourTeamId = ourTeamId
	c.cortex = cortex
	cortex.Run()
}

func (c *Checkerlution) GameFinished(gameState cbot.GameState) (shouldQuit bool) {
	logg.LogTo("MAIN", "GameFinished")
	switch c.mode {
	case TRAINING_MODE:
		logg.LogTo("MAIN", "in TRAINING_MODE, so quitting")
		shouldQuit = true
		c.latestFitnessScore = c.calculateFitness(gameState)
	case RUNNING_MODE:
		logg.LogTo("MAIN", "in RUNNIG_MODE, so not quitting")
		shouldQuit = false
	}
	logg.LogTo("MAIN", "shouldQuit: %v", shouldQuit)
	return
}

func (c *Checkerlution) Think(gameState cbot.GameState) (bestMove cbot.ValidMove, ok bool) {
	ok = true
	gameStateVector := c.extractGameStateVector(gameState)
	possibleMoves := c.extractPossibleMoves(gameState)
	if len(possibleMoves) == 0 {
		logg.LogTo("MAIN", "No possibleMoves, ignoring changes")
		ok = false
		return
	}
	bestMoveCortex := c.chooseBestMove(gameStateVector, possibleMoves)
	bestMove = bestMoveCortex.validMove
	return
}

func (c Checkerlution) Cortex() *ng.Cortex {
	return c.cortex
}

func (c *Checkerlution) SetMode(mode int) {
	c.mode = mode
}

func (c Checkerlution) calculateFitness(gameState cbot.GameState) (fitness float64) {
	weWon := (gameState.WinningTeam == c.ourTeamId)
	switch weWon {
	case true:
		logg.LogTo("MAIN", "calculateFitness based on winning")
		// fitness will be a positive number
		// the least amount of moves we made, the higher the fitness
		fitness = 200
		fitness -= float64(gameState.Turn)
		if fitness < 1 {
			fitness = 1 // lowest possible fitness when winning
		}
	case false:
		logg.LogTo("MAIN", "calculateFitness based on losing")
		// fitness will be a negative number
		// the least amount of moves we made, the lower (more negative)
		// the fitness, because we didn't put up much of a fight
		fitness = -200
		fitness += float64(gameState.Turn)
		if fitness > -1 {
			fitness = -1 // highest possible fitness when losing
		}
	}

	logg.LogTo("MAIN", "calculateFitness returning: %v", fitness)
	return
}

func (c Checkerlution) extractGameStateVector(gameState cbot.GameState) GameStateVector {
	gameStateVector := NewGameStateVector()
	gameStateVector.loadFromGameState(gameState, c.ourTeamId)
	return gameStateVector
}

func (c Checkerlution) extractPossibleMoves(gameState cbot.GameState) []ValidMoveCortexInput {

	moves := make([]ValidMoveCortexInput, 0)

	ourTeam := gameState.Teams[c.ourTeamId]

	for pieceIndex, piece := range ourTeam.Pieces {
		for _, validMove := range piece.ValidMoves {

			// enhance the validMove from some information
			// from the piece, because this will be required
			// later when translating this valid move into an
			// "outgoing move", eg, a move that can be posted
			// to server to cause it to make.  the outgoing move
			// is a pretty different format than the orig validMove
			validMove.StartLocation = piece.Location
			validMove.PieceId = pieceIndex

			moveInput := NewValidMoveCortexInput(validMove, piece)
			moves = append(moves, moveInput)
		}
	}

	return moves
}

func (c *Checkerlution) chooseBestMove(gameStateVector GameStateVector, possibleMoves []ValidMoveCortexInput) (bestMove ValidMoveCortexInput) {

	c.currentGameState = gameStateVector
	logg.LogTo("MAIN", "gameStateVector: %v", gameStateVector)

	var bestMoveRating []float64
	bestMoveRating = []float64{-1000000000}

	for _, move := range possibleMoves {

		logg.LogTo("MAIN", "feed possible move to cortex: %v", move)

		// present it to the neural net
		c.currentPossibleMove = move
		c.cortex.SyncSensors()
		c.cortex.SyncActuators()

		logg.LogTo("MAIN", "done sync'ing actuators")

		logg.LogTo("MAIN", "actuator output %v bestMoveRating: %v", c.latestActuatorOutput[0], bestMoveRating[0])
		if c.latestActuatorOutput[0] > bestMoveRating[0] {
			logg.LogTo("MAIN", "actuator output > bestMoveRating")
			bestMove = move
			bestMoveRating[0] = c.latestActuatorOutput[0]
		} else {
			logg.LogTo("MAIN", "actuator output < bestMoveRating, ignoring")
		}

	}
	return

}

func (c *Checkerlution) CreateNeurgoCortex() {

	nodeId := ng.NewCortexId("cortex")
	c.cortex = &ng.Cortex{
		NodeId: nodeId,
	}
	c.CreateSensors()
	c.CreateActuator()
	c.CreateNeuron()
	c.ConnectNodes()
}

func (c *Checkerlution) LoadNeurgoCortex(filename string) {

	// TODO: move this code to cortex!!

	file, err := os.Open(filename)
	if err != nil {
		logg.LogPanic("Error opening file: %v", err)
	}

	cortex := &ng.Cortex{}
	jsonParser := json.NewDecoder(file)
	if err = jsonParser.Decode(cortex); err != nil {
		logg.LogPanic("Error parsing file: %v", err)
	}
	c.cortex = cortex

}

func (c *Checkerlution) ConnectNodes() {

	cortex := c.cortex

	cortex.Init()

	// connect sensors -> neuron(s)
	for _, sensor := range cortex.Sensors {
		for _, neuron := range cortex.Neurons {
			sensor.ConnectOutbound(neuron)
			weights := ng.RandomWeights(sensor.VectorLength)
			neuron.ConnectInboundWeighted(sensor, weights)
		}
	}

	// connect neuron to actuator
	for _, neuron := range cortex.Neurons {
		for _, actuator := range cortex.Actuators {
			neuron.ConnectOutbound(actuator)
			actuator.ConnectInbound(neuron)
		}
	}

}

func (c *Checkerlution) CreateNeuron() {
	neuron := &ng.Neuron{
		ActivationFunction: ng.EncodableTanh(),
		NodeId:             ng.NewNeuronId("Neuron", 0.25),
		Bias:               ng.RandomBias(),
	}
	c.cortex.SetNeurons([]*ng.Neuron{neuron})
}

func (c *Checkerlution) CreateActuator() {

	actuatorNodeId := ng.NewActuatorId("Actuator", 0.5)
	actuatorFunc := func(outputs []float64) {
		logg.LogTo("MAIN", "actuator func called with: %v", outputs)
		c.latestActuatorOutput = outputs
	}
	actuator := &ng.Actuator{
		NodeId:           actuatorNodeId,
		VectorLength:     1,
		ActuatorFunction: actuatorFunc,
	}
	c.cortex.SetActuators([]*ng.Actuator{actuator})

}

func (c *Checkerlution) CreateSensors() {

	sensorLayer := 0.0

	sensorFuncGameState := func(syncCounter int) []float64 {
		logg.LogTo("MAIN", "sensor func game state called")
		return c.currentGameState
	}
	sensorGameStateNodeId := ng.NewSensorId("SensorGameState", sensorLayer)
	sensorGameState := &ng.Sensor{
		NodeId:         sensorGameStateNodeId,
		VectorLength:   32,
		SensorFunction: sensorFuncGameState,
	}

	sensorFuncPossibleMove := func(syncCounter int) []float64 {
		logg.LogTo("MAIN", "sensor func possible move called")
		return c.currentPossibleMove.VectorRepresentation()
	}
	sensorPossibleMoveNodeId := ng.NewSensorId("SensorPossibleMove", sensorLayer)
	sensorPossibleMove := &ng.Sensor{
		NodeId:         sensorPossibleMoveNodeId,
		VectorLength:   5, // start_location, is_king, final_location, will_be_king, amt_would_capture
		SensorFunction: sensorFuncPossibleMove,
	}
	c.cortex.SetSensors([]*ng.Sensor{sensorGameState, sensorPossibleMove})

}

func (c Checkerlution) Stop() {

}
<commit_msg>change mode from int -> OperationMode<commit_after>package checkerlution

import (
	"encoding/json"
	"github.com/couchbaselabs/logg"
	cbot "github.com/tleyden/checkers-bot"
	ng "github.com/tleyden/neurgo"
	"os"
)

type OperationMode int

const (
	RUNNING_MODE = iota
	TRAINING_MODE
)

type Checkerlution struct {
	ourTeamId            cbot.TeamType
	cortex               *ng.Cortex
	currentGameState     GameStateVector
	currentPossibleMove  ValidMoveCortexInput
	latestActuatorOutput []float64
	mode                 OperationMode
	latestFitnessScore   float64
}

func (c *Checkerlution) Start(ourTeamId cbot.TeamType) {
	c.ourTeamId = ourTeamId
	c.CreateNeurgoCortex()
	cortex := c.cortex
	cortex.Run()

}

func (c *Checkerlution) StartWithCortex(cortex *ng.Cortex, ourTeamId cbot.TeamType) {
	c.ourTeamId = ourTeamId
	c.cortex = cortex
	cortex.Run()
}

func (c *Checkerlution) GameFinished(gameState cbot.GameState) (shouldQuit bool) {
	logg.LogTo("MAIN", "GameFinished")
	switch c.mode {
	case TRAINING_MODE:
		logg.LogTo("MAIN", "in TRAINING_MODE, so quitting")
		shouldQuit = true
		c.latestFitnessScore = c.calculateFitness(gameState)
	case RUNNING_MODE:
		logg.LogTo("MAIN", "in RUNNIG_MODE, so not quitting")
		shouldQuit = false
	}
	logg.LogTo("MAIN", "shouldQuit: %v", shouldQuit)
	return
}

func (c *Checkerlution) Think(gameState cbot.GameState) (bestMove cbot.ValidMove, ok bool) {
	ok = true
	gameStateVector := c.extractGameStateVector(gameState)
	possibleMoves := c.extractPossibleMoves(gameState)
	if len(possibleMoves) == 0 {
		logg.LogTo("MAIN", "No possibleMoves, ignoring changes")
		ok = false
		return
	}
	bestMoveCortex := c.chooseBestMove(gameStateVector, possibleMoves)
	bestMove = bestMoveCortex.validMove
	return
}

func (c Checkerlution) Cortex() *ng.Cortex {
	return c.cortex
}

func (c *Checkerlution) SetMode(mode OperationMode) {
	c.mode = mode
}

func (c Checkerlution) calculateFitness(gameState cbot.GameState) (fitness float64) {
	weWon := (gameState.WinningTeam == c.ourTeamId)
	switch weWon {
	case true:
		logg.LogTo("MAIN", "calculateFitness based on winning")
		// fitness will be a positive number
		// the least amount of moves we made, the higher the fitness
		fitness = 200
		fitness -= float64(gameState.Turn)
		if fitness < 1 {
			fitness = 1 // lowest possible fitness when winning
		}
	case false:
		logg.LogTo("MAIN", "calculateFitness based on losing")
		// fitness will be a negative number
		// the least amount of moves we made, the lower (more negative)
		// the fitness, because we didn't put up much of a fight
		fitness = -200
		fitness += float64(gameState.Turn)
		if fitness > -1 {
			fitness = -1 // highest possible fitness when losing
		}
	}

	logg.LogTo("MAIN", "calculateFitness returning: %v", fitness)
	return
}

func (c Checkerlution) extractGameStateVector(gameState cbot.GameState) GameStateVector {
	gameStateVector := NewGameStateVector()
	gameStateVector.loadFromGameState(gameState, c.ourTeamId)
	return gameStateVector
}

func (c Checkerlution) extractPossibleMoves(gameState cbot.GameState) []ValidMoveCortexInput {

	moves := make([]ValidMoveCortexInput, 0)

	ourTeam := gameState.Teams[c.ourTeamId]

	for pieceIndex, piece := range ourTeam.Pieces {
		for _, validMove := range piece.ValidMoves {

			// enhance the validMove from some information
			// from the piece, because this will be required
			// later when translating this valid move into an
			// "outgoing move", eg, a move that can be posted
			// to server to cause it to make.  the outgoing move
			// is a pretty different format than the orig validMove
			validMove.StartLocation = piece.Location
			validMove.PieceId = pieceIndex

			moveInput := NewValidMoveCortexInput(validMove, piece)
			moves = append(moves, moveInput)
		}
	}

	return moves
}

func (c *Checkerlution) chooseBestMove(gameStateVector GameStateVector, possibleMoves []ValidMoveCortexInput) (bestMove ValidMoveCortexInput) {

	c.currentGameState = gameStateVector
	logg.LogTo("MAIN", "gameStateVector: %v", gameStateVector)

	var bestMoveRating []float64
	bestMoveRating = []float64{-1000000000}

	for _, move := range possibleMoves {

		logg.LogTo("MAIN", "feed possible move to cortex: %v", move)

		// present it to the neural net
		c.currentPossibleMove = move
		c.cortex.SyncSensors()
		c.cortex.SyncActuators()

		logg.LogTo("MAIN", "done sync'ing actuators")

		logg.LogTo("MAIN", "actuator output %v bestMoveRating: %v", c.latestActuatorOutput[0], bestMoveRating[0])
		if c.latestActuatorOutput[0] > bestMoveRating[0] {
			logg.LogTo("MAIN", "actuator output > bestMoveRating")
			bestMove = move
			bestMoveRating[0] = c.latestActuatorOutput[0]
		} else {
			logg.LogTo("MAIN", "actuator output < bestMoveRating, ignoring")
		}

	}
	return

}

func (c *Checkerlution) CreateNeurgoCortex() {

	nodeId := ng.NewCortexId("cortex")
	c.cortex = &ng.Cortex{
		NodeId: nodeId,
	}
	c.CreateSensors()
	c.CreateActuator()
	c.CreateNeuron()
	c.ConnectNodes()
}

func (c *Checkerlution) LoadNeurgoCortex(filename string) {

	// TODO: move this code to cortex!!

	file, err := os.Open(filename)
	if err != nil {
		logg.LogPanic("Error opening file: %v", err)
	}

	cortex := &ng.Cortex{}
	jsonParser := json.NewDecoder(file)
	if err = jsonParser.Decode(cortex); err != nil {
		logg.LogPanic("Error parsing file: %v", err)
	}
	c.cortex = cortex

}

func (c *Checkerlution) ConnectNodes() {

	cortex := c.cortex

	cortex.Init()

	// connect sensors -> neuron(s)
	for _, sensor := range cortex.Sensors {
		for _, neuron := range cortex.Neurons {
			sensor.ConnectOutbound(neuron)
			weights := ng.RandomWeights(sensor.VectorLength)
			neuron.ConnectInboundWeighted(sensor, weights)
		}
	}

	// connect neuron to actuator
	for _, neuron := range cortex.Neurons {
		for _, actuator := range cortex.Actuators {
			neuron.ConnectOutbound(actuator)
			actuator.ConnectInbound(neuron)
		}
	}

}

func (c *Checkerlution) CreateNeuron() {
	neuron := &ng.Neuron{
		ActivationFunction: ng.EncodableTanh(),
		NodeId:             ng.NewNeuronId("Neuron", 0.25),
		Bias:               ng.RandomBias(),
	}
	c.cortex.SetNeurons([]*ng.Neuron{neuron})
}

func (c *Checkerlution) CreateActuator() {

	actuatorNodeId := ng.NewActuatorId("Actuator", 0.5)
	actuatorFunc := func(outputs []float64) {
		logg.LogTo("MAIN", "actuator func called with: %v", outputs)
		c.latestActuatorOutput = outputs
	}
	actuator := &ng.Actuator{
		NodeId:           actuatorNodeId,
		VectorLength:     1,
		ActuatorFunction: actuatorFunc,
	}
	c.cortex.SetActuators([]*ng.Actuator{actuator})

}

func (c *Checkerlution) CreateSensors() {

	sensorLayer := 0.0

	sensorFuncGameState := func(syncCounter int) []float64 {
		logg.LogTo("MAIN", "sensor func game state called")
		return c.currentGameState
	}
	sensorGameStateNodeId := ng.NewSensorId("SensorGameState", sensorLayer)
	sensorGameState := &ng.Sensor{
		NodeId:         sensorGameStateNodeId,
		VectorLength:   32,
		SensorFunction: sensorFuncGameState,
	}

	sensorFuncPossibleMove := func(syncCounter int) []float64 {
		logg.LogTo("MAIN", "sensor func possible move called")
		return c.currentPossibleMove.VectorRepresentation()
	}
	sensorPossibleMoveNodeId := ng.NewSensorId("SensorPossibleMove", sensorLayer)
	sensorPossibleMove := &ng.Sensor{
		NodeId:         sensorPossibleMoveNodeId,
		VectorLength:   5, // start_location, is_king, final_location, will_be_king, amt_would_capture
		SensorFunction: sensorFuncPossibleMove,
	}
	c.cortex.SetSensors([]*ng.Sensor{sensorGameState, sensorPossibleMove})

}

func (c Checkerlution) Stop() {

}
<|endoftext|>


<commit_before>package chunk

import (
	"fmt"

	"net/http"

	. "github.com/claudetech/loggo/default"

	"time"

	"github.com/dweidenfeld/plexdrive/drive"
)

// Manager manages chunks on disk
type Manager struct {
	ChunkPath      string
	ChunkSize      int64
	LoadAhead      int
	Timeout        time.Duration
	TimeoutRetries int
	downloader     *Downloader
	queue          chan *Request
	storage        *Storage
}

// Request represents a chunk request
type Request struct {
	id          string
	object      *drive.APIObject
	preload     bool
	offsetStart int64
	offsetEnd   int64
}

// NewManager creates a new chunk manager
func NewManager(
	chunkPath string,
	chunkSize int64,
	loadAhead,
	threads int,
	client *http.Client,
	maxChunks int,
	timeout time.Duration,
	timeoutRetries int) (*Manager, error) {

	if "" == chunkPath {
		return nil, fmt.Errorf("Path to chunk file must not be empty")
	}
	if chunkSize < 4096 {
		return nil, fmt.Errorf("Chunk size must not be < 4096")
	}
	if chunkSize%1024 != 0 {
		return nil, fmt.Errorf("Chunk size must be divideable by 1024")
	}
	if maxChunks < 2 || maxChunks < loadAhead {
		return nil, fmt.Errorf("max-chunk must be greater than 2 and bigger than the load ahead value")
	}

	downloader, err := NewDownloader(threads, client)
	if nil != err {
		return nil, err
	}

	manager := Manager{
		ChunkPath:      chunkPath,
		ChunkSize:      chunkSize,
		LoadAhead:      loadAhead,
		Timeout:        timeout,
		TimeoutRetries: timeoutRetries,
		downloader:     downloader,
		queue:          make(chan *Request, 500),
		storage:        NewStorage(chunkPath, chunkSize, maxChunks),
	}

	if err := manager.storage.Clear(); nil != err {
		return nil, err
	}

	for i := 0; i < threads; i++ {
		go manager.thread(i)
	}

	return &manager, nil
}

// GetChunk loads one chunk and starts the preload for the next chunks
func (m *Manager) GetChunk(object *drive.APIObject, offset, size int64) ([]byte, error) {
	chunkOffset := offset % m.ChunkSize
	offsetStart := offset - chunkOffset
	offsetEnd := offsetStart + m.ChunkSize
	id := fmt.Sprintf("%v:%v", object.ObjectID, offsetStart)

	m.queue <- &Request{
		id:          id,
		object:      object,
		offsetStart: offsetStart,
		offsetEnd:   offsetEnd,
		preload:     false,
	}

	for i := m.ChunkSize; i < (m.ChunkSize * int64(m.LoadAhead+1)); i += m.ChunkSize {
		aheadOffsetStart := offsetStart + i
		aheadOffsetEnd := aheadOffsetStart + m.ChunkSize
		if uint64(aheadOffsetStart) < object.Size && uint64(aheadOffsetEnd) < object.Size {
			id := fmt.Sprintf("%v:%v", object.ObjectID, aheadOffsetStart)
			m.queue <- &Request{
				id:          id,
				object:      object,
				offsetStart: aheadOffsetStart,
				offsetEnd:   aheadOffsetEnd,
				preload:     true,
			}
		}
	}

	bytes, err := m.storage.Get(id, chunkOffset, size, m.Timeout)
	retryCount := 0
	for err == ErrTimeout && retryCount < m.TimeoutRetries {
		Log.Warningf("Timeout while requesting chunk %v. Retrying (%v / %v)", id, (retryCount + 1), m.TimeoutRetries)
		bytes, err = m.storage.Get(id, chunkOffset, size, m.Timeout)
		retryCount++
	}
	return bytes, err
}

func (m *Manager) thread(threadID int) {
	for {
		m.checkChunk(<-m.queue, threadID)
	}
}

func (m *Manager) checkChunk(req *Request, threadID int) {
	if m.storage.ExistsOrCreate(req.id) {
		return
	}

	Log.Debugf("Requesting object %v (%v) bytes %v - %v from API (preload: %v | thread: %v)",
		req.object.ObjectID, req.object.Name, req.offsetStart, req.offsetEnd, req.preload, threadID)

	bytes, err := m.downloader.Download(req)
	if nil != err {
		Log.Warningf("%v", err)
		m.storage.Error(req.id, err)
	}

	if err := m.storage.Store(req.id, bytes); nil != err {
		Log.Warningf("%v", err)
	}
}
<commit_msg>chunk existence check is handled in main thread<commit_after>package chunk

import (
	"fmt"

	"net/http"

	. "github.com/claudetech/loggo/default"

	"time"

	"github.com/dweidenfeld/plexdrive/drive"
)

// Manager manages chunks on disk
type Manager struct {
	ChunkPath      string
	ChunkSize      int64
	LoadAhead      int
	Timeout        time.Duration
	TimeoutRetries int
	downloader     *Downloader
	queue          chan *Request
	storage        *Storage
}

// Request represents a chunk request
type Request struct {
	id          string
	object      *drive.APIObject
	preload     bool
	offsetStart int64
	offsetEnd   int64
}

// NewManager creates a new chunk manager
func NewManager(
	chunkPath string,
	chunkSize int64,
	loadAhead,
	threads int,
	client *http.Client,
	maxChunks int,
	timeout time.Duration,
	timeoutRetries int) (*Manager, error) {

	if "" == chunkPath {
		return nil, fmt.Errorf("Path to chunk file must not be empty")
	}
	if chunkSize < 4096 {
		return nil, fmt.Errorf("Chunk size must not be < 4096")
	}
	if chunkSize%1024 != 0 {
		return nil, fmt.Errorf("Chunk size must be divideable by 1024")
	}
	if maxChunks < 2 || maxChunks < loadAhead {
		return nil, fmt.Errorf("max-chunk must be greater than 2 and bigger than the load ahead value")
	}

	downloader, err := NewDownloader(threads, client)
	if nil != err {
		return nil, err
	}

	manager := Manager{
		ChunkPath:      chunkPath,
		ChunkSize:      chunkSize,
		LoadAhead:      loadAhead,
		Timeout:        timeout,
		TimeoutRetries: timeoutRetries,
		downloader:     downloader,
		queue:          make(chan *Request, 500),
		storage:        NewStorage(chunkPath, chunkSize, maxChunks),
	}

	if err := manager.storage.Clear(); nil != err {
		return nil, err
	}

	for i := 0; i < threads; i++ {
		go manager.thread(i)
	}

	return &manager, nil
}

// GetChunk loads one chunk and starts the preload for the next chunks
func (m *Manager) GetChunk(object *drive.APIObject, offset, size int64) ([]byte, error) {
	chunkOffset := offset % m.ChunkSize
	offsetStart := offset - chunkOffset
	offsetEnd := offsetStart + m.ChunkSize
	id := fmt.Sprintf("%v:%v", object.ObjectID, offsetStart)

	if !m.storage.ExistsOrCreate(id) {
		m.queue <- &Request{
			id:          id,
			object:      object,
			offsetStart: offsetStart,
			offsetEnd:   offsetEnd,
			preload:     false,
		}
	}

	for i := m.ChunkSize; i < (m.ChunkSize * int64(m.LoadAhead+1)); i += m.ChunkSize {
		aheadOffsetStart := offsetStart + i
		aheadOffsetEnd := aheadOffsetStart + m.ChunkSize
		if uint64(aheadOffsetStart) < object.Size && uint64(aheadOffsetEnd) < object.Size {
			id := fmt.Sprintf("%v:%v", object.ObjectID, aheadOffsetStart)
			if !m.storage.ExistsOrCreate(id) {
				m.queue <- &Request{
					id:          id,
					object:      object,
					offsetStart: aheadOffsetStart,
					offsetEnd:   aheadOffsetEnd,
					preload:     true,
				}
			}
		}
	}

	bytes, err := m.storage.Get(id, chunkOffset, size, m.Timeout)
	retryCount := 0
	for err == ErrTimeout && retryCount < m.TimeoutRetries {
		Log.Warningf("Timeout while requesting chunk %v. Retrying (%v / %v)", id, (retryCount + 1), m.TimeoutRetries)
		bytes, err = m.storage.Get(id, chunkOffset, size, m.Timeout)
		retryCount++
	}
	return bytes, err
}

func (m *Manager) thread(threadID int) {
	for {
		m.checkChunk(<-m.queue, threadID)
	}
}

func (m *Manager) checkChunk(req *Request, threadID int) {
	Log.Debugf("Requesting object %v (%v) bytes %v - %v from API (preload: %v | thread: %v)",
		req.object.ObjectID, req.object.Name, req.offsetStart, req.offsetEnd, req.preload, threadID)

	bytes, err := m.downloader.Download(req)
	if nil != err {
		Log.Warningf("%v", err)
		m.storage.Error(req.id, err)
	}

	if err := m.storage.Store(req.id, bytes); nil != err {
		Log.Warningf("%v", err)
	}
}
<|endoftext|>


<commit_before>package rest

import (
	"github.com/geodan/gost/src/sensorthings/models"
	"github.com/stretchr/testify/assert"
	"strings"
	"testing"
)

func TestCreateEndPoints(t *testing.T) {
	//arrange
	endpoints := CreateEndPoints("http://test.com")

	//assert
	assert.Equal(t, len(endpoints), 10)
}

func TestCreateEndPointVersion(t *testing.T) {
	//arrange
	ve := createVersion("http://test.com")

	//assert
	containsVersionPath := containsEndpoint("Version", ve.Operations)
	assert.Equal(t, true, containsVersionPath, "Version endpoint needs to contain an endpoint containing the path Version")
}

func containsEndpoint(epName string, eps []models.EndpointOperation) bool {
	for _, o := range eps {
		if strings.Contains(o.Path, "Version") {
			return true
		}
	}

	return false
}
<commit_msg>fix test<commit_after>package rest

import (
	"github.com/geodan/gost/src/sensorthings/models"
	"github.com/stretchr/testify/assert"
	"strings"
	"testing"
)

func TestCreateEndPoints(t *testing.T) {
	//arrange
	endpoints := CreateEndPoints("http://test.com")

	//assert
	assert.Equal(t, len(endpoints), 9)
}

func TestCreateEndPointVersion(t *testing.T) {
	//arrange
	ve := createVersion("http://test.com")

	//assert
	containsVersionPath := containsEndpoint("Version", ve.Operations)
	assert.Equal(t, true, containsVersionPath, "Version endpoint needs to contain an endpoint containing the path Version")
}

func containsEndpoint(epName string, eps []models.EndpointOperation) bool {
	for _, o := range eps {
		if strings.Contains(o.Path, "Version") {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>// Package database provides an sqlx DB connection to execute SQL commands on
// an SQLite3 database named "cabin.db".
package database

import (
	"database/sql"
	"fmt"
	"sync"

	"github.com/jmoiron/sqlx"
	_ "github.com/mattn/go-sqlite3" // sqlite3 driver
	"github.com/sirupsen/logrus"
)

// NewConnection opens the SQLite3 database and creates our table if necessary.
func NewConnection(databasePath *string) (db *sqlx.DB, mutex *sync.Mutex) {
	mutex = &sync.Mutex{}
	db, err := sqlx.Open("sqlite3", ":memory:")
	if err != nil {
		panic(err)
	}

	connString := fmt.Sprintf("file:%s?cache=private&mode=rwc", *databasePath)
	sqlite, err := sql.Open("sqlite3", connString)
	if err != nil {
		panic(err)
	}
	db = sqlx.NewDb(sqlite, "sqlite3")

	enableForeignKeys(db)

	logrus.Info("Ensuring database tables exist...")
	ensureReadingsTable(db)
	ensureUsersTable(db)
	ensureAnomaliesTable(db)
	ensureAnomaliesUsersTable(db)
	ensureHistoricalTable(db)
	ensureLogbookTable(db)
	return
}

func enableForeignKeys(db *sqlx.DB) {
	logrus.Info("Enabling database foreign keys...")
	foreignKeySchema := `PRAGMA foreign_keys = ON;`
	_, err := db.Exec(foreignKeySchema)
	if err != nil {
		panic(err)
	}
}

func ensureLogbookTable(db *sqlx.DB) {
	schema := `CREATE TABLE IF NOT EXISTS logbook (author TEXT, body TEXT, created TIMESTAMP DEFAULT CURRENT_TIMESTAMP);`
	_, err := db.Exec(schema)
	if err != nil {
		panic(err)
	}
}

func ensureHistoricalTable(db *sqlx.DB) {
	schema := `CREATE TABLE IF NOT EXISTS historical_files (name TEXT);
	CREATE INDEX IF NOT EXISTS idx_name ON historical_files (name);`

	_, err := db.Exec(schema)
	if err != nil {
		panic(err)
	}
}

// ensureReadingsTable checks for the readings table. Creates it if necessary.
func ensureReadingsTable(db *sqlx.DB) {
	schema := `CREATE TABLE IF NOT EXISTS readings (
		timestamp         TIMESTAMP,
		unix              INTEGER,
		battery_voltage   FLOAT64,
		solar_voltage     FLOAT64,
		battery_amperage  FLOAT64,
		load_amperage     FLOAT64,
		solar_amperage 	  FLOAT64,
		avg_load_power    FLOAT64,
		avg_solar_power   FLOAT64,
		avg_hydro_power   FLOAT64,
		outside_temp      FLOAT64,
		cabin_temp        FLOAT64,
		battery_temp      FLOAT64
		);
		CREATE INDEX IF NOT EXISTS idx_unix_readings
		ON readings (unix);`

	// execute a query on the server
	_, err := db.Exec(schema)
	if err != nil {
		panic(err)
	}
}

func ensureAnomaliesTable(db *sqlx.DB) {
	schema := `CREATE TABLE IF NOT EXISTS anomalies (
		id			  	  INTEGER NOT NULL,
		name			  TEXT,
		timestamp         TIMESTAMP,
		unix              INTEGER,
		battery_voltage   FLOAT64,
		solar_voltage     FLOAT64,
		battery_amperage  FLOAT64,
		load_amperage     FLOAT64,
		solar_amperage 	  FLOAT64,
		avg_load_power    FLOAT64,
		avg_solar_power   FLOAT64,
		avg_hydro_power   FLOAT64,
		outside_temp      FLOAT64,
		cabin_temp        FLOAT64,
		battery_temp      FLOAT64,
		PRIMARY KEY(id)
	);
	CREATE INDEX IF NOT EXISTS idx_unix_anomalies ON anomalies (unix);
	CREATE INDEX IF NOT EXISTS idx_name_anomalies ON anomalies (name);`

	_, err := db.Exec(schema)
	if err != nil {
		panic(err)
	}
}

func ensureAnomaliesUsersTable(db *sqlx.DB) {
	schema := `CREATE TABLE IF NOT EXISTS anomalies_users (
		user_id    INTEGER,
		anomaly_id INTEGER,
		FOREIGN KEY (user_id) REFERENCES users(id),
		FOREIGN KEY (anomaly_id) REFERENCES anomalies(id),
		UNIQUE(user_id, anomaly_id)
	);
	CREATE INDEX IF NOT EXISTS idx_user_anomaliesusers ON anomalies_users (user_id);
	CREATE INDEX IF NOT EXISTS idx_anomaly_anomaliesusers ON anomalies_users (anomaly_id);`

	_, err := db.Exec(schema)
	if err != nil {
		panic(err)
	}
}

// ensureUsersTable checks for the users table. Creates it if necessary
func ensureUsersTable(db *sqlx.DB) {
	schema := `CREATE TABLE IF NOT EXISTS users (
		id		 INTEGER PRIMARY KEY,
		name 	 TEXT,
		email 	 TEXT UNIQUE,
		password TEXT,
		type     TEXT
	);
	CREATE INDEX IF NOT EXISTS idx_email_users ON users (email);`

	_, err := db.Exec(schema)
	if err != nil {
		panic(err)
	}
}
<commit_msg>Remove cache argument for db connection<commit_after>// Package database provides an sqlx DB connection to execute SQL commands on
// an SQLite3 database named "cabin.db".
package database

import (
	"database/sql"
	"fmt"
	"sync"

	"github.com/jmoiron/sqlx"
	_ "github.com/mattn/go-sqlite3" // sqlite3 driver
	"github.com/sirupsen/logrus"
)

// NewConnection opens the SQLite3 database and creates our table if necessary.
func NewConnection(databasePath *string) (db *sqlx.DB, mutex *sync.Mutex) {
	mutex = &sync.Mutex{}
	db, err := sqlx.Open("sqlite3", ":memory:")
	if err != nil {
		panic(err)
	}

	connString := fmt.Sprintf("file:%s?&mode=rwc", *databasePath)
	sqlite, err := sql.Open("sqlite3", connString)
	if err != nil {
		panic(err)
	}
	db = sqlx.NewDb(sqlite, "sqlite3")

	enableForeignKeys(db)

	logrus.Info("Ensuring database tables exist...")
	ensureReadingsTable(db)
	ensureUsersTable(db)
	ensureAnomaliesTable(db)
	ensureAnomaliesUsersTable(db)
	ensureHistoricalTable(db)
	ensureLogbookTable(db)
	return
}

func enableForeignKeys(db *sqlx.DB) {
	logrus.Info("Enabling database foreign keys...")
	foreignKeySchema := `PRAGMA foreign_keys = ON;`
	_, err := db.Exec(foreignKeySchema)
	if err != nil {
		panic(err)
	}
}

func ensureLogbookTable(db *sqlx.DB) {
	schema := `CREATE TABLE IF NOT EXISTS logbook (author TEXT, body TEXT, created TIMESTAMP DEFAULT CURRENT_TIMESTAMP);`
	_, err := db.Exec(schema)
	if err != nil {
		panic(err)
	}
}

func ensureHistoricalTable(db *sqlx.DB) {
	schema := `CREATE TABLE IF NOT EXISTS historical_files (name TEXT);
	CREATE INDEX IF NOT EXISTS idx_name ON historical_files (name);`

	_, err := db.Exec(schema)
	if err != nil {
		panic(err)
	}
}

// ensureReadingsTable checks for the readings table. Creates it if necessary.
func ensureReadingsTable(db *sqlx.DB) {
	schema := `CREATE TABLE IF NOT EXISTS readings (
		timestamp         TIMESTAMP,
		unix              INTEGER,
		battery_voltage   FLOAT64,
		solar_voltage     FLOAT64,
		battery_amperage  FLOAT64,
		load_amperage     FLOAT64,
		solar_amperage 	  FLOAT64,
		avg_load_power    FLOAT64,
		avg_solar_power   FLOAT64,
		avg_hydro_power   FLOAT64,
		outside_temp      FLOAT64,
		cabin_temp        FLOAT64,
		battery_temp      FLOAT64
		);
		CREATE INDEX IF NOT EXISTS idx_unix_readings
		ON readings (unix);`

	// execute a query on the server
	_, err := db.Exec(schema)
	if err != nil {
		panic(err)
	}
}

func ensureAnomaliesTable(db *sqlx.DB) {
	schema := `CREATE TABLE IF NOT EXISTS anomalies (
		id			  	  INTEGER NOT NULL,
		name			  TEXT,
		timestamp         TIMESTAMP,
		unix              INTEGER,
		battery_voltage   FLOAT64,
		solar_voltage     FLOAT64,
		battery_amperage  FLOAT64,
		load_amperage     FLOAT64,
		solar_amperage 	  FLOAT64,
		avg_load_power    FLOAT64,
		avg_solar_power   FLOAT64,
		avg_hydro_power   FLOAT64,
		outside_temp      FLOAT64,
		cabin_temp        FLOAT64,
		battery_temp      FLOAT64,
		PRIMARY KEY(id)
	);
	CREATE INDEX IF NOT EXISTS idx_unix_anomalies ON anomalies (unix);
	CREATE INDEX IF NOT EXISTS idx_name_anomalies ON anomalies (name);`

	_, err := db.Exec(schema)
	if err != nil {
		panic(err)
	}
}

func ensureAnomaliesUsersTable(db *sqlx.DB) {
	schema := `CREATE TABLE IF NOT EXISTS anomalies_users (
		user_id    INTEGER,
		anomaly_id INTEGER,
		FOREIGN KEY (user_id) REFERENCES users(id),
		FOREIGN KEY (anomaly_id) REFERENCES anomalies(id),
		UNIQUE(user_id, anomaly_id)
	);
	CREATE INDEX IF NOT EXISTS idx_user_anomaliesusers ON anomalies_users (user_id);
	CREATE INDEX IF NOT EXISTS idx_anomaly_anomaliesusers ON anomalies_users (anomaly_id);`

	_, err := db.Exec(schema)
	if err != nil {
		panic(err)
	}
}

// ensureUsersTable checks for the users table. Creates it if necessary
func ensureUsersTable(db *sqlx.DB) {
	schema := `CREATE TABLE IF NOT EXISTS users (
		id		 INTEGER PRIMARY KEY,
		name 	 TEXT,
		email 	 TEXT UNIQUE,
		password TEXT,
		type     TEXT
	);
	CREATE INDEX IF NOT EXISTS idx_email_users ON users (email);`

	_, err := db.Exec(schema)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package router

import (
	"github.com/freehaha/go-r3"
	"github.com/gorilla/context"
	"log"
	"net/http"
	"runtime"
)

type Router struct {
	tree            *r3.Tree
	NotFoundHandler http.Handler
}

var methods map[string]r3.Method = map[string]r3.Method{
	"GET":     r3.MethodGet,
	"PUT":     r3.MethodPut,
	"POST":    r3.MethodPost,
	"DELETE":  r3.MethodDelete,
	"OPTIONS": r3.MethodOptions,
	"PATCH":   r3.MethodPatch,
}

/* instanciate a new Router */
func NewRouter() *Router {
	r := &Router{}
	r.tree = r3.NewTree(10)
	runtime.SetFinalizer(r, finalizeRouter)
	return r
}

func finalizeRouter(r *Router) {
	log.Print("finalizing router")
	r.tree.Free()
}

func (r *Router) Compile() error {
	return r.tree.Compile()
}

func (r *Router) Free() {
	finalizeRouter(r)
}

/* insert a path to the router with specific handler */
func (r *Router) HandleFunc(method r3.Method, path string, handler http.HandlerFunc) {
	r.tree.InsertRoute(method, path, handler)
}

/* implement Mux */
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ent := r3.NewMatchEntry(req.URL.Path)
	ent.SetRequestMethod(methods[req.Method])
	log.Print(req.URL.Path)
	log.Print(req.Method)
	defer ent.Free()
	route := r.tree.MatchRoute(ent)
	if route != nil {
		context.Set(req, "vars", ent.Vars())
		handler := route.Data().(http.HandlerFunc)
		handler(w, req)
	} else {
		handler := r.NotFoundHandler
		if handler == nil {
			handler = http.NotFoundHandler()
		}
		handler.ServeHTTP(w, req)
	}
}
<commit_msg>Add Vars() to access path variables<commit_after>package router

import (
	"github.com/freehaha/go-r3"
	"github.com/gorilla/context"
	"log"
	"net/http"
	"runtime"
)

type Router struct {
	tree            *r3.Tree
	NotFoundHandler http.Handler
}

var methods map[string]r3.Method = map[string]r3.Method{
	"GET":     r3.MethodGet,
	"PUT":     r3.MethodPut,
	"POST":    r3.MethodPost,
	"DELETE":  r3.MethodDelete,
	"OPTIONS": r3.MethodOptions,
	"PATCH":   r3.MethodPatch,
}

/* instanciate a new Router */
func NewRouter() *Router {
	r := &Router{}
	r.tree = r3.NewTree(10)
	runtime.SetFinalizer(r, finalizeRouter)
	return r
}

func finalizeRouter(r *Router) {
	log.Print("finalizing router")
	r.tree.Free()
}

func (r *Router) Compile() error {
	return r.tree.Compile()
}

func (r *Router) Free() {
	finalizeRouter(r)
}

func Vars(r *http.Request) []string {
	return context.Get(r, "vars").([]string)
}

/* insert a path to the router with specific handler */
func (r *Router) HandleFunc(method r3.Method, path string, handler http.HandlerFunc) {
	r.tree.InsertRoute(method, path, handler)
}

/* implement Mux */
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ent := r3.NewMatchEntry(req.URL.Path)
	ent.SetRequestMethod(methods[req.Method])
	log.Print(req.URL.Path)
	log.Print(req.Method)
	defer ent.Free()
	route := r.tree.MatchRoute(ent)
	if route != nil {
		context.Set(req, "vars", ent.Vars())
		handler := route.Data().(http.HandlerFunc)
		handler(w, req)
	} else {
		handler := r.NotFoundHandler
		if handler == nil {
			handler = http.NotFoundHandler()
		}
		handler.ServeHTTP(w, req)
	}
}
<|endoftext|>


<commit_before>package chart

import (
	"image/color"
	"testing"

	"github.com/blendlabs/go-assert"
	"github.com/wcharczuk/go-chart/drawing"
)

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

	as := AnnotationSeries{
		Style: Style{
			Show: true,
		},
		Annotations: []Annotation{
			Annotation{X: 1.0, Y: 1.0, Label: "1.0"},
			Annotation{X: 2.0, Y: 2.0, Label: "2.0"},
			Annotation{X: 3.0, Y: 3.0, Label: "3.0"},
			Annotation{X: 4.0, Y: 4.0, Label: "4.0"},
		},
	}

	r, err := PNG(110, 110)
	assert.Nil(err)

	f, err := GetDefaultFont()
	assert.Nil(err)

	xrange := Range{
		Min:    1.0,
		Max:    4.0,
		Domain: 100,
	}
	yrange := Range{
		Min:    1.0,
		Max:    4.0,
		Domain: 100,
	}

	cb := Box{
		Top:    5,
		Left:   5,
		Right:  105,
		Bottom: 105,
		Height: 100,
		Width:  100,
	}
	sd := Style{
		FontSize: 10.0,
		Font:     f,
	}

	box := as.Measure(r, cb, xrange, yrange, sd)
	assert.False(box.IsZero())
	assert.Equal(-5.0, box.Top)
	assert.Equal(5.0, box.Left)
	assert.Equal(145.0, box.Right) //the top,left annotation sticks up 5px and out ~44px.
	assert.Equal(115.0, box.Bottom)
}

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

	as := AnnotationSeries{
		Style: Style{
			Show:        true,
			FillColor:   drawing.ColorWhite,
			StrokeColor: drawing.ColorBlack,
		},
		Annotations: []Annotation{
			Annotation{X: 1.0, Y: 1.0, Label: "1.0"},
			Annotation{X: 2.0, Y: 2.0, Label: "2.0"},
			Annotation{X: 3.0, Y: 3.0, Label: "3.0"},
			Annotation{X: 4.0, Y: 4.0, Label: "4.0"},
		},
	}

	r, err := PNG(110, 110)
	assert.Nil(err)

	f, err := GetDefaultFont()
	assert.Nil(err)

	xrange := Range{
		Min:    1.0,
		Max:    4.0,
		Domain: 100,
	}
	yrange := Range{
		Min:    1.0,
		Max:    4.0,
		Domain: 100,
	}

	cb := Box{
		Top:    5,
		Left:   5,
		Right:  105,
		Bottom: 105,
		Height: 100,
		Width:  100,
	}
	sd := Style{
		FontSize: 10.0,
		Font:     f,
	}

	as.Render(r, cb, xrange, yrange, sd)

	rr, isRaster := r.(*rasterRenderer)
	assert.True(isRaster)
	assert.NotNil(rr)

	c := rr.i.At(39, 70)
	converted, isRGBA := color.RGBAModel.Convert(c).(color.RGBA)
	assert.True(isRGBA)
	assert.Equal(3, converted.R)
	assert.Equal(3, converted.G)
	assert.Equal(3, converted.B)
}
<commit_msg>fixing test.<commit_after>package chart

import (
	"image/color"
	"testing"

	"github.com/blendlabs/go-assert"
	"github.com/wcharczuk/go-chart/drawing"
)

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

	as := AnnotationSeries{
		Style: Style{
			Show: true,
		},
		Annotations: []Annotation{
			Annotation{X: 1.0, Y: 1.0, Label: "1.0"},
			Annotation{X: 2.0, Y: 2.0, Label: "2.0"},
			Annotation{X: 3.0, Y: 3.0, Label: "3.0"},
			Annotation{X: 4.0, Y: 4.0, Label: "4.0"},
		},
	}

	r, err := PNG(110, 110)
	assert.Nil(err)

	f, err := GetDefaultFont()
	assert.Nil(err)

	xrange := Range{
		Min:    1.0,
		Max:    4.0,
		Domain: 100,
	}
	yrange := Range{
		Min:    1.0,
		Max:    4.0,
		Domain: 100,
	}

	cb := Box{
		Top:    5,
		Left:   5,
		Right:  105,
		Bottom: 105,
		Height: 100,
		Width:  100,
	}
	sd := Style{
		FontSize: 10.0,
		Font:     f,
	}

	box := as.Measure(r, cb, xrange, yrange, sd)
	assert.False(box.IsZero())
	assert.Equal(-5.0, box.Top)
	assert.Equal(5.0, box.Left)
	assert.Equal(146.0, box.Right) //the top,left annotation sticks up 5px and out ~44px.
	assert.Equal(115.0, box.Bottom)
}

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

	as := AnnotationSeries{
		Style: Style{
			Show:        true,
			FillColor:   drawing.ColorWhite,
			StrokeColor: drawing.ColorBlack,
		},
		Annotations: []Annotation{
			Annotation{X: 1.0, Y: 1.0, Label: "1.0"},
			Annotation{X: 2.0, Y: 2.0, Label: "2.0"},
			Annotation{X: 3.0, Y: 3.0, Label: "3.0"},
			Annotation{X: 4.0, Y: 4.0, Label: "4.0"},
		},
	}

	r, err := PNG(110, 110)
	assert.Nil(err)

	f, err := GetDefaultFont()
	assert.Nil(err)

	xrange := Range{
		Min:    1.0,
		Max:    4.0,
		Domain: 100,
	}
	yrange := Range{
		Min:    1.0,
		Max:    4.0,
		Domain: 100,
	}

	cb := Box{
		Top:    5,
		Left:   5,
		Right:  105,
		Bottom: 105,
		Height: 100,
		Width:  100,
	}
	sd := Style{
		FontSize: 10.0,
		Font:     f,
	}

	as.Render(r, cb, xrange, yrange, sd)

	rr, isRaster := r.(*rasterRenderer)
	assert.True(isRaster)
	assert.NotNil(rr)

	c := rr.i.At(39, 70)
	converted, isRGBA := color.RGBAModel.Convert(c).(color.RGBA)
	assert.True(isRGBA)
	assert.Equal(3, converted.R)
	assert.Equal(3, converted.G)
	assert.Equal(3, converted.B)
}
<|endoftext|>


<commit_before>// Package cliutil contains methods used across all cli commands
// @todo: get rid of os.Exits and use errors instread
package cliutil

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"strings"

	ccli "github.com/micro/cli/v2"
	"github.com/micro/go-micro/v2/util/config"
	"github.com/micro/micro/v2/internal/platform"
	"github.com/micro/micro/v2/runtime/profile"
)

const (
	// EnvLocal is a builtin environment, it means services launched
	// with `micro run` will use default, zero dependency implementations for
	// interfaces, like mdns for registry.
	EnvLocal = "local"
	// EnvServer is a builtin environment, it represents your local `micro server`
	EnvServer = "server"
	// EnvPlatform is a builtin environment, the One True Micro Live(tm) environment.
	EnvPlatform = "platform"
)

const (
	// localProxyAddress is the default proxy address for environment local
	// local env does not use other services so talking about a proxy
	localProxyAddress = "none"
	// serverProxyAddress is the default proxy address for environment server
	serverProxyAddress = "127.0.0.1:8081"
	// platformProxyAddress is teh default proxy address for environment platform
	platformProxyAddress = "proxy.micro.mu"
)

var defaultEnvs = map[string]Env{
	EnvLocal: Env{
		Name:         EnvLocal,
		ProxyAddress: localProxyAddress,
	},
	EnvServer: Env{
		Name:         EnvServer,
		ProxyAddress: serverProxyAddress,
	},
	EnvPlatform: Env{
		Name:         EnvPlatform,
		ProxyAddress: platformProxyAddress,
	},
}

func isBuiltinService(command string) bool {
	if command == "server" {
		return true
	}
	for _, service := range platform.Services {
		if command == service {
			return true
		}
	}
	return false
}

// SetupCommand includes things that should run for each command.
func SetupCommand(ctx *ccli.Context) {
	if ctx.Args().Len() == 1 && isBuiltinService(ctx.Args().First()) {
		return
	}
	if ctx.Args().Len() >= 1 && ctx.Args().First() == "env" {
		return
	}

	toFlag := func(s string) string {
		return strings.ToLower(strings.ReplaceAll(s, "MICRO_", ""))
	}
	setFlags := func(envars []string) {
		for _, envar := range envars {
			// setting both env and flags here
			// as the proxy settings for example did not take effect
			// with only flags
			parts := strings.Split(envar, "=")
			key := toFlag(parts[0])
			os.Setenv(parts[0], parts[1])
			ctx.Set(key, parts[1])
		}
	}
	env := GetEnv()
	switch env.Name {
	case EnvServer:
		setFlags(profile.ServerCLI())
	case EnvPlatform:
		setFlags(profile.PlatformCLI())
	case EnvLocal:
		// Not setting a proxy for local env
		return
	}

	// Set proxy for all envs apart from local
	setFlags([]string{"MICRO_PROXY=service", "MICRO_PROXY_ADDRESS=" + env.ProxyAddress})
}

type Env struct {
	Name         string
	ProxyAddress string
}

func AddEnv(env Env) {
	envs := getEnvs()
	envs[env.Name] = env
	setEnvs(envs)
}

func getEnvs() map[string]Env {
	envsJSON, err := config.Get("envs")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	envs := map[string]Env{}
	if len(envsJSON) > 0 {
		err := json.Unmarshal([]byte(envsJSON), &envs)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	}
	for k, v := range defaultEnvs {
		envs[k] = v
	}
	return envs
}

func setEnvs(envs map[string]Env) {
	envsJSON, err := json.Marshal(envs)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	err = config.Set(string(envsJSON), "envs")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

// GetEnv returns the current selected environment
func GetEnv() Env {
	env, err := config.Get("env")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	envs := getEnvs()
	envir, ok := envs[env]
	if !ok {
		return defaultEnvs[EnvLocal]
	}

	// default to :443
	if _, port, _ := net.SplitHostPort(envir.ProxyAddress); len(port) == 0 {
		envir.ProxyAddress = net.JoinHostPort(envir.ProxyAddress, "443")
	}

	return envir
}

func GetEnvs() []Env {
	envs := getEnvs()
	ret := []Env{defaultEnvs[EnvLocal], defaultEnvs[EnvServer], defaultEnvs[EnvPlatform]}
	nonDefaults := []Env{}
	for _, env := range envs {
		if _, isDefault := defaultEnvs[env.Name]; !isDefault {
			nonDefaults = append(nonDefaults, env)
		}
	}
	// @todo order nondefault envs alphabetically
	ret = append(ret, nonDefaults...)
	return ret
}

// SetEnv selects an environment to be used.
func SetEnv(envName string) {
	envs := getEnvs()
	_, ok := envs[envName]
	if !ok {
		fmt.Printf("Environment '%v' does not exist\n", envName)
		os.Exit(1)
	}
	config.Set(envName, "env")
}

func IsLocal() bool {
	return GetEnv().Name == EnvLocal
}

func IsServer() bool {
	return GetEnv().Name == EnvServer
}

func IsPlatform() bool {
	return GetEnv().Name == EnvPlatform
}
<commit_msg>dont set proxy address in cli<commit_after>// Package cliutil contains methods used across all cli commands
// @todo: get rid of os.Exits and use errors instread
package cliutil

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"strings"

	ccli "github.com/micro/cli/v2"
	"github.com/micro/go-micro/v2/util/config"
	"github.com/micro/micro/v2/internal/platform"
	"github.com/micro/micro/v2/runtime/profile"
)

const (
	// EnvLocal is a builtin environment, it means services launched
	// with `micro run` will use default, zero dependency implementations for
	// interfaces, like mdns for registry.
	EnvLocal = "local"
	// EnvServer is a builtin environment, it represents your local `micro server`
	EnvServer = "server"
	// EnvPlatform is a builtin environment, the One True Micro Live(tm) environment.
	EnvPlatform = "platform"
)

const (
	// localProxyAddress is the default proxy address for environment local
	// local env does not use other services so talking about a proxy
	localProxyAddress = "none"
	// serverProxyAddress is the default proxy address for environment server
	serverProxyAddress = "127.0.0.1:8081"
	// platformProxyAddress is teh default proxy address for environment platform
	platformProxyAddress = "proxy.micro.mu"
)

var defaultEnvs = map[string]Env{
	EnvLocal: Env{
		Name:         EnvLocal,
		ProxyAddress: localProxyAddress,
	},
	EnvServer: Env{
		Name:         EnvServer,
		ProxyAddress: serverProxyAddress,
	},
	EnvPlatform: Env{
		Name:         EnvPlatform,
		ProxyAddress: platformProxyAddress,
	},
}

func isBuiltinService(command string) bool {
	if command == "server" {
		return true
	}
	for _, service := range platform.Services {
		if command == service {
			return true
		}
	}
	return false
}

// SetupCommand includes things that should run for each command.
func SetupCommand(ctx *ccli.Context) {
	if ctx.Args().Len() == 1 && isBuiltinService(ctx.Args().First()) {
		return
	}
	if ctx.Args().Len() >= 1 && ctx.Args().First() == "env" {
		return
	}

	toFlag := func(s string) string {
		return strings.ToLower(strings.ReplaceAll(s, "MICRO_", ""))
	}
	setFlags := func(envars []string) {
		for _, envar := range envars {
			// setting both env and flags here
			// as the proxy settings for example did not take effect
			// with only flags
			parts := strings.Split(envar, "=")
			key := toFlag(parts[0])
			os.Setenv(parts[0], parts[1])
			ctx.Set(key, parts[1])
		}
	}
	env := GetEnv()
	switch env.Name {
	case EnvServer:
		setFlags(profile.ServerCLI())
	case EnvPlatform:
		setFlags(profile.PlatformCLI())
	case EnvLocal:
		// Not setting a proxy for local env
		return
	}

	// Set proxy for all envs apart from local
	setFlags([]string{"MICRO_PROXY=" + env.ProxyAddress})
}

type Env struct {
	Name         string
	ProxyAddress string
}

func AddEnv(env Env) {
	envs := getEnvs()
	envs[env.Name] = env
	setEnvs(envs)
}

func getEnvs() map[string]Env {
	envsJSON, err := config.Get("envs")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	envs := map[string]Env{}
	if len(envsJSON) > 0 {
		err := json.Unmarshal([]byte(envsJSON), &envs)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	}
	for k, v := range defaultEnvs {
		envs[k] = v
	}
	return envs
}

func setEnvs(envs map[string]Env) {
	envsJSON, err := json.Marshal(envs)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	err = config.Set(string(envsJSON), "envs")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

// GetEnv returns the current selected environment
func GetEnv() Env {
	env, err := config.Get("env")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	envs := getEnvs()
	envir, ok := envs[env]
	if !ok {
		return defaultEnvs[EnvLocal]
	}

	// default to :443
	if _, port, _ := net.SplitHostPort(envir.ProxyAddress); len(port) == 0 {
		envir.ProxyAddress = net.JoinHostPort(envir.ProxyAddress, "443")
	}

	return envir
}

func GetEnvs() []Env {
	envs := getEnvs()
	ret := []Env{defaultEnvs[EnvLocal], defaultEnvs[EnvServer], defaultEnvs[EnvPlatform]}
	nonDefaults := []Env{}
	for _, env := range envs {
		if _, isDefault := defaultEnvs[env.Name]; !isDefault {
			nonDefaults = append(nonDefaults, env)
		}
	}
	// @todo order nondefault envs alphabetically
	ret = append(ret, nonDefaults...)
	return ret
}

// SetEnv selects an environment to be used.
func SetEnv(envName string) {
	envs := getEnvs()
	_, ok := envs[envName]
	if !ok {
		fmt.Printf("Environment '%v' does not exist\n", envName)
		os.Exit(1)
	}
	config.Set(envName, "env")
}

func IsLocal() bool {
	return GetEnv().Name == EnvLocal
}

func IsServer() bool {
	return GetEnv().Name == EnvServer
}

func IsPlatform() bool {
	return GetEnv().Name == EnvPlatform
}
<|endoftext|>


<commit_before>package oppobloom

import (
	"bytes"
	"crypto/md5"
	"errors"
	"hash"
	"math"
	"sync/atomic"
	"unsafe"
)

type Filter struct {
	array []*[]byte
	sizeMask uint32
}

var ErrSizeTooLarge = errors.New("oppobloom: size given to large to round to a power of 2")
var ErrSizeTooSmall = errors.New("oppobloom: filter cannot have a zero or negative size")
var MaxFilterSize = 1 << 30

func NewFilter(size int) (*Filter, error) {
	if size > MaxFilterSize {
		return nil, ErrSizeTooLarge
	}
	if size <= 0 {
		return nil, ErrSizeTooSmall
	}
	// round to the next largest power of two
	size = int(math.Pow(2, math.Ceil(math.Log2(float64(size)))))
	slice := make([]*[]byte, size)
	sizeMask := uint32(size - 1)
	return &Filter{slice, sizeMask}, nil
}

func (f *Filter) Contains(id []byte) bool {
	h := md5UintHash{md5.New()}
	h.Write(id)
	uindex := h.Sum32() & f.sizeMask
	index := int32(uindex)
	oldId := getAndSet(f.array, index, id)
	return bytes.Equal(oldId, id)
}

func (f *Filter) Size() int {
	return len(f.array)
}

type md5UintHash struct {
	hash.Hash // a hack with knowledge of how md5 works
}

func (m md5UintHash) Sum32() uint32 {
	sum := m.Sum(nil)
	x := uint32(sum[0])
	for _, val := range sum[1:4] {
		x = x << 3
		x += uint32(val)
	}
	return x
}

// Returns the id that was in the slice at the given index after putting the
// new id in the slice at that index, atomically.
func getAndSet(arr []*[]byte, index int32, id []byte) []byte {
	indexPtr := (*unsafe.Pointer)(unsafe.Pointer(&arr[index]))
	idUnsafe := unsafe.Pointer(&id)
	var oldId []byte
	for {
		oldIdUnsafe := atomic.LoadPointer(indexPtr)
		if atomic.CompareAndSwapPointer(indexPtr, oldIdUnsafe, idUnsafe) {
			oldIdPtr := (*[]byte)(oldIdUnsafe)
			if oldIdPtr != nil {
				oldId = *oldIdPtr
			}
			break
		}
	}
	return oldId
}
<commit_msg>go: iterate only over the 3 bytes we need<commit_after>package oppobloom

import (
	"bytes"
	"crypto/md5"
	"errors"
	"hash"
	"math"
	"sync/atomic"
	"unsafe"
)

type Filter struct {
	array []*[]byte
	sizeMask uint32
}

var ErrSizeTooLarge = errors.New("oppobloom: size given to large to round to a power of 2")
var ErrSizeTooSmall = errors.New("oppobloom: filter cannot have a zero or negative size")
var MaxFilterSize = 1 << 30

func NewFilter(size int) (*Filter, error) {
	if size > MaxFilterSize {
		return nil, ErrSizeTooLarge
	}
	if size <= 0 {
		return nil, ErrSizeTooSmall
	}
	// round to the next largest power of two
	size = int(math.Pow(2, math.Ceil(math.Log2(float64(size)))))
	slice := make([]*[]byte, size)
	sizeMask := uint32(size - 1)
	return &Filter{slice, sizeMask}, nil
}

func (f *Filter) Contains(id []byte) bool {
	h := md5UintHash{md5.New()}
	h.Write(id)
	uindex := h.Sum32() & f.sizeMask
	index := int32(uindex)
	oldId := getAndSet(f.array, index, id)
	return bytes.Equal(oldId, id)
}

func (f *Filter) Size() int {
	return len(f.array)
}

type md5UintHash struct {
	hash.Hash // a hack with knowledge of how md5 works
}

func (m md5UintHash) Sum32() uint32 {
	sum := m.Sum(nil)
	x := uint32(sum[0])
	for _, val := range sum[1:3] {
		x = x << 3
		x += uint32(val)
	}
	return x
}

// Returns the id that was in the slice at the given index after putting the
// new id in the slice at that index, atomically.
func getAndSet(arr []*[]byte, index int32, id []byte) []byte {
	indexPtr := (*unsafe.Pointer)(unsafe.Pointer(&arr[index]))
	idUnsafe := unsafe.Pointer(&id)
	var oldId []byte
	for {
		oldIdUnsafe := atomic.LoadPointer(indexPtr)
		if atomic.CompareAndSwapPointer(indexPtr, oldIdUnsafe, idUnsafe) {
			oldIdPtr := (*[]byte)(oldIdUnsafe)
			if oldIdPtr != nil {
				oldId = *oldIdPtr
			}
			break
		}
	}
	return oldId
}
<|endoftext|>


<commit_before>// Copyright 2016 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package sysfs

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/maruel/dlibox/go/pio"
	"github.com/maruel/dlibox/go/pio/protocols/gpio"
)

// Pins is all the pins exported by GPIO sysfs.
var Pins map[int]*Pin

// PinByNumber returns a *Pin for the pin number, if any.
func PinByNumber(i int) (*Pin, error) {
	p, ok := Pins[i]
	if !ok {
		return nil, errors.New("invalid pin number")
	}
	if err := p.open(); err != nil {
		return nil, err
	}
	return p, nil
}

// Pin represents one GPIO pin as found by sysfs.
type Pin struct {
	number int
	name   string
	root   string // Something like /sys/class/gpio/gpio%d/

	lock       sync.Mutex
	direction  int             // Cache of the last known direction
	fDirection *os.File        // handle to /sys/class/gpio/gpio*/direction; never closed
	fEdge      *os.File        // handle to /sys/class/gpio/gpio*/edge; never closed
	fValue     *os.File        // handle to /sys/class/gpio/gpio*/value; never closed
	epollFd    int             // Never closed
	event      event           // Initialized once
	edges      chan gpio.Level // Closed when edges are terminated
	wg         sync.WaitGroup  // Set when Edges() is running
}

func (p *Pin) String() string {
	return p.name
}

func (p *Pin) Number() int {
	return p.number
}

func (p *Pin) Function() string {
	p.lock.Lock()
	defer p.lock.Unlock()
	// TODO(maruel): There's an internal bug which causes p.direction to be invalid (!?)
	// Need to figure it out ASAP.
	if err := p.open(); err != nil {
		return err.Error()
	}
	var buf [4]byte
	if _, err := p.fDirection.Seek(0, 0); err != nil {
		return err.Error()
	}
	if _, err := p.fDirection.Read(buf[:]); err != nil {
		return err.Error()
	}
	if buf[0] == 'i' && buf[1] == 'n' {
		p.direction = dIn
	} else if buf[0] == 'o' && buf[1] == 'u' && buf[2] == 't' {
		p.direction = dOut
	}
	if p.direction == dIn {
		return "In/" + p.Read().String()
	} else if p.direction == dOut {
		return "Out/" + p.Read().String()
	}
	return "N/A"
}

// In setups a pin as an input.
func (p *Pin) In(pull gpio.Pull) error {
	if pull != gpio.PullNoChange && pull != gpio.Float {
		return errors.New("not implemented")
	}
	p.lock.Lock()
	defer p.lock.Unlock()
	if p.direction == dIn {
		return nil
	}
	if err := p.open(); err != nil {
		return err
	}
	if _, err := p.fDirection.Seek(0, 0); err != nil {
		return err
	}
	if _, err := p.fDirection.Write([]byte("in")); err != nil {
		return err
	}
	p.direction = dIn
	return nil
}

func (p *Pin) Read() gpio.Level {
	var buf [2]byte
	if _, err := p.fValue.Seek(0, 0); err != nil {
		// Error.
		//fmt.Printf("%s: %v", p, err)
		return gpio.Low
	}
	if _, err := p.fValue.Read(buf[:]); err != nil {
		// Error.
		//fmt.Printf("%s: %v", p, err)
		return gpio.Low
	}
	if buf[0] == '0' {
		return gpio.Low
	}
	if buf[0] == '1' {
		return gpio.High
	}
	// Error.
	return gpio.Low
}

// Edges creates a edge detection loop and implements gpio.PinIn.
//
// It is the function that opens the gpio sysfs file handle for /edge.
func (p *Pin) Edges() (<-chan gpio.Level, error) {
	p.lock.Lock()
	defer p.lock.Unlock()
	if p.edges != nil {
		return nil, errors.New("must call DisableEdges() between Edges()")
	}
	var err error
	if p.fEdge == nil {
		p.fEdge, err = os.OpenFile(p.root+"edge", os.O_RDWR|os.O_APPEND, 0600)
		if err != nil {
			return nil, err
		}
	}
	if p.epollFd == 0 {
		if p.epollFd, err = p.event.makeEvent(p.fValue); err != nil {
			return nil, err
		}
	}
	if _, err := p.fEdge.Seek(0, 0); err != nil {
		return nil, err
	}
	if _, err = p.fEdge.Write([]byte("both")); err != nil {
		return nil, err
	}
	p.edges = make(chan gpio.Level)
	p.wg.Add(1)
	var started sync.WaitGroup
	started.Add(1)
	go p.edgeLoop(&started)
	started.Wait()
	return p.edges, nil
}

// DisableEdges stops a previous Edges() call.
func (p *Pin) DisableEdges() {
	p.lock.Lock()
	defer p.lock.Unlock()
	p.disableEdge()
}

func (p *Pin) Pull() gpio.Pull {
	return gpio.PullNoChange
}

// Out sets a pin as output.
func (p Pin) Out(l gpio.Level) error {
	p.lock.Lock()
	defer p.lock.Unlock()
	if p.direction != dOut {
		if err := p.open(); err != nil {
			return err
		}
		// Cancel any outstanding edge detection.
		p.disableEdge()

		// "To ensure glitch free operation, values "low" and "high" may be written
		// to configure the GPIO as an output with that initial value."
		if _, err := p.fDirection.Seek(0, 0); err != nil {
			return err
		}
		var d []byte
		if l == gpio.Low {
			d = []byte("low")
		} else {
			d = []byte("high")
		}
		if _, err := p.fDirection.Write(d); err != nil {
			return err
		}
		p.direction = dOut
		return nil
	}
	if _, err := p.fValue.Seek(0, 0); err != nil {
		return nil
	}
	var d [2]byte
	if l == gpio.Low {
		d[0] = '0'
	} else {
		d[0] = '1'
	}
	_, err := p.fValue.Write(d[:])
	return err
}

//

// open opens the gpio sysfs handle to /value and direction.
//
// lock must be held.
func (p *Pin) open() error {
	if exportHandle == nil {
		return errors.New("sysfs gpio is not initialized")
	}
	if p.fDirection != nil {
		return nil
	}
	// Ignore the failure unless this is a permission failure. Exporting a pin
	// that is already exported causes a write failure.
	_, err := exportHandle.Write([]byte(fmt.Sprintf("%d\n", p.number)))
	if os.IsPermission(err) {
		return err
	}
	// There's a race condition where the file may be created but udev is still
	// running the script to make it readable to the current user. It's simpler
	// to just loop a little as if /export is accessible, it doesn't make sense
	// that gpioN/value doesn't become accessible eventually.
	timeout := 5 * time.Second
	for start := time.Now(); time.Since(start) < timeout; {
		p.fValue, err = os.OpenFile(p.root+"value", os.O_RDWR, 0600)
		// The virtual file creation is synchronous when writing to /export for
		// udev rule execution is asynchronous.
		if err == nil || !os.IsPermission(err) {
			break
		}
	}
	p.fDirection, err = os.OpenFile(p.root+"direction", os.O_RDWR|os.O_APPEND, 0600)
	if err != nil {
		p.fValue.Close()
		p.fValue = nil
	}
	return err
}

// disableEdge disable the edge detection setting for the pin, if any.
func (p *Pin) disableEdge() error {
	if p.direction != dIn {
		return errors.New("pin wasn't set as input first")
	}
	if p.edges != nil {
		// Drain it if needed. This works because p.edges is not buffered.
		select {
		case <-p.edges:
		default:
		}
		// Only after it is safe to close.
		close(p.edges)
		p.edges = nil
		if _, err := p.fEdge.Seek(0, 0); err != nil {
			return err
		}
		if _, err := p.fEdge.Write([]byte("none")); err != nil {
			return err
		}
	}
	return nil
}

func (p *Pin) edgeLoop(started *sync.WaitGroup) {
	defer p.wg.Done()
	last := p.Read()
	started.Done()
	var b [1]byte
	for {
		if _, err := p.fValue.Seek(0, 0); err != nil {
			log.Printf("edgeLoop() ended: %v\n", err)
			return
		}
		for {
			p.lock.Lock()
			c := p.edges
			p.lock.Unlock()
			if c == nil {
				return
			}
			if nr, err := p.event.wait(p.epollFd); err != nil {
				log.Printf("edgeLoop() ended: %v\n", err)
				return
			} else if nr < 1 {
				continue
			}
			if _, err := p.fValue.Seek(0, 0); err != nil {
				log.Printf("edgeLoop() ended: %v\n", err)
				return
			}
			if _, err := p.fValue.Read(b[:]); err != nil {
				log.Printf("edgeLoop() ended: %v\n", err)
				return
			}
			break
		}
		p.lock.Lock()
		c := p.edges
		p.lock.Unlock()
		if c == nil {
			return
		}
		// Make sure to ignore spurious wake up.
		if b[0] == '1' {
			if last != gpio.High {
				c <- gpio.High
				last = gpio.High
			}
		} else {
			if last != gpio.Low {
				c <- gpio.Low
				last = gpio.Low
			}
		}
	}
}

//

var exportHandle io.Writer // handle to /sys/class/gpio/export

const (
	dUnknown = 0
	dIn      = 1
	dOut     = 2
)

func readInt(path string) (int, error) {
	raw, err := ioutil.ReadFile(path)
	if err != nil {
		return 0, err
	}
	if len(raw) == 0 || raw[len(raw)-1] != '\n' {
		return 0, errors.New("invalid value")
	}
	return strconv.Atoi(string(raw[:len(raw)-1]))
}

// driverGPIO implements pio.Driver.
type driverGPIO struct {
}

func (d *driverGPIO) String() string {
	return "sysfs-gpio"
}

func (d *driverGPIO) Type() pio.Type {
	// It intentionally load later than processors.
	return pio.Pins
}

func (d *driverGPIO) Prerequisites() []string {
	return nil
}

// Init initializes GPIO sysfs handling code.
//
// Uses gpio sysfs as described at
// https://www.kernel.org/doc/Documentation/gpio/sysfs.txt
//
// GPIO sysfs is often the only way to do edge triggered interrupts. Doing this
// requires cooperation from a driver in the kernel.
//
// The main drawback of GPIO sysfs is that it doesn't expose internal pull
// resistor and it is much slower than using memory mapped hardware registers.
func (d *driverGPIO) Init() (bool, error) {
	f, err := os.OpenFile("/sys/class/gpio/export", os.O_WRONLY, 0600)
	if err != nil {
		return true, err
	}
	items, err := ioutil.ReadDir("/sys/class/gpio/")
	if err != nil {
		f.Close()
		return true, err
	}
	// There are hosts that use non-continuous pin numbering.
	Pins = map[int]*Pin{}
	for _, item := range items {
		name := item.Name()
		if !strings.HasPrefix(name, "gpiochip") {
			continue
		}
		if err := d.exportGPIOChip("/sys/class/gpio/" + name + "/"); err != nil {
			f.Close()
			return true, err
		}
	}
	exportHandle = f
	return true, nil
}

func (d *driverGPIO) exportGPIOChip(path string) error {
	base, err := readInt(path + "base")
	if err != nil {
		return err
	}
	number, err := readInt(path + "ngpio")
	if err != nil {
		return err
	}
	// TODO(maruel): The chip driver may lie and lists GPIO pins that cannot be
	// exported. The only way to know about it is to export it before opening.
	for i := base; i < base+number; i++ {
		if _, ok := Pins[i]; ok {
			return fmt.Errorf("found two pins with number %d", i)
		}
		p := &Pin{
			number: i,
			name:   fmt.Sprintf("GPIO%d", i),
			root:   fmt.Sprintf("/sys/class/gpio/gpio%d/", i),
		}
		Pins[i] = p
		// Intentionally ignore any error. In that case, a Processor driver already
		// registered the pin.
		_ = gpio.Register(p)
		// We cannot use gpio.MapFunction() since there is no API to determine this.
	}
	return nil
}
<commit_msg>Seek() before waiting for an event, not after.<commit_after>// Copyright 2016 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package sysfs

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/maruel/dlibox/go/pio"
	"github.com/maruel/dlibox/go/pio/protocols/gpio"
)

// Pins is all the pins exported by GPIO sysfs.
var Pins map[int]*Pin

// PinByNumber returns a *Pin for the pin number, if any.
func PinByNumber(i int) (*Pin, error) {
	p, ok := Pins[i]
	if !ok {
		return nil, errors.New("invalid pin number")
	}
	if err := p.open(); err != nil {
		return nil, err
	}
	return p, nil
}

// Pin represents one GPIO pin as found by sysfs.
type Pin struct {
	number int
	name   string
	root   string // Something like /sys/class/gpio/gpio%d/

	lock       sync.Mutex
	direction  int             // Cache of the last known direction
	fDirection *os.File        // handle to /sys/class/gpio/gpio*/direction; never closed
	fEdge      *os.File        // handle to /sys/class/gpio/gpio*/edge; never closed
	fValue     *os.File        // handle to /sys/class/gpio/gpio*/value; never closed
	epollFd    int             // Never closed
	event      event           // Initialized once
	edges      chan gpio.Level // Closed when edges are terminated
	wg         sync.WaitGroup  // Set when Edges() is running
}

func (p *Pin) String() string {
	return p.name
}

func (p *Pin) Number() int {
	return p.number
}

func (p *Pin) Function() string {
	p.lock.Lock()
	defer p.lock.Unlock()
	// TODO(maruel): There's an internal bug which causes p.direction to be invalid (!?)
	// Need to figure it out ASAP.
	if err := p.open(); err != nil {
		return err.Error()
	}
	var buf [4]byte
	if _, err := p.fDirection.Seek(0, 0); err != nil {
		return err.Error()
	}
	if _, err := p.fDirection.Read(buf[:]); err != nil {
		return err.Error()
	}
	if buf[0] == 'i' && buf[1] == 'n' {
		p.direction = dIn
	} else if buf[0] == 'o' && buf[1] == 'u' && buf[2] == 't' {
		p.direction = dOut
	}
	if p.direction == dIn {
		return "In/" + p.Read().String()
	} else if p.direction == dOut {
		return "Out/" + p.Read().String()
	}
	return "N/A"
}

// In setups a pin as an input.
func (p *Pin) In(pull gpio.Pull) error {
	if pull != gpio.PullNoChange && pull != gpio.Float {
		return errors.New("not implemented")
	}
	p.lock.Lock()
	defer p.lock.Unlock()
	if p.direction == dIn {
		return nil
	}
	if err := p.open(); err != nil {
		return err
	}
	if _, err := p.fDirection.Seek(0, 0); err != nil {
		return err
	}
	if _, err := p.fDirection.Write([]byte("in")); err != nil {
		return err
	}
	p.direction = dIn
	return nil
}

func (p *Pin) Read() gpio.Level {
	var buf [2]byte
	if _, err := p.fValue.Seek(0, 0); err != nil {
		// Error.
		//fmt.Printf("%s: %v", p, err)
		return gpio.Low
	}
	if _, err := p.fValue.Read(buf[:]); err != nil {
		// Error.
		//fmt.Printf("%s: %v", p, err)
		return gpio.Low
	}
	if buf[0] == '0' {
		return gpio.Low
	}
	if buf[0] == '1' {
		return gpio.High
	}
	// Error.
	return gpio.Low
}

// Edges creates a edge detection loop and implements gpio.PinIn.
//
// It is the function that opens the gpio sysfs file handle for /edge.
func (p *Pin) Edges() (<-chan gpio.Level, error) {
	p.lock.Lock()
	defer p.lock.Unlock()
	if p.edges != nil {
		return nil, errors.New("must call DisableEdges() between Edges()")
	}
	var err error
	if p.fEdge == nil {
		p.fEdge, err = os.OpenFile(p.root+"edge", os.O_RDWR|os.O_APPEND, 0600)
		if err != nil {
			return nil, err
		}
	}
	if p.epollFd == 0 {
		if p.epollFd, err = p.event.makeEvent(p.fValue); err != nil {
			return nil, err
		}
	}
	if _, err := p.fEdge.Seek(0, 0); err != nil {
		return nil, err
	}
	if _, err = p.fEdge.Write([]byte("both")); err != nil {
		return nil, err
	}
	p.edges = make(chan gpio.Level)
	p.wg.Add(1)
	var started sync.WaitGroup
	started.Add(1)
	go p.edgeLoop(&started)
	started.Wait()
	return p.edges, nil
}

// DisableEdges stops a previous Edges() call.
func (p *Pin) DisableEdges() {
	p.lock.Lock()
	defer p.lock.Unlock()
	p.disableEdge()
}

func (p *Pin) Pull() gpio.Pull {
	return gpio.PullNoChange
}

// Out sets a pin as output.
func (p Pin) Out(l gpio.Level) error {
	p.lock.Lock()
	defer p.lock.Unlock()
	if p.direction != dOut {
		if err := p.open(); err != nil {
			return err
		}
		// Cancel any outstanding edge detection.
		p.disableEdge()

		// "To ensure glitch free operation, values "low" and "high" may be written
		// to configure the GPIO as an output with that initial value."
		if _, err := p.fDirection.Seek(0, 0); err != nil {
			return err
		}
		var d []byte
		if l == gpio.Low {
			d = []byte("low")
		} else {
			d = []byte("high")
		}
		if _, err := p.fDirection.Write(d); err != nil {
			return err
		}
		p.direction = dOut
		return nil
	}
	if _, err := p.fValue.Seek(0, 0); err != nil {
		return nil
	}
	var d [2]byte
	if l == gpio.Low {
		d[0] = '0'
	} else {
		d[0] = '1'
	}
	_, err := p.fValue.Write(d[:])
	return err
}

//

// open opens the gpio sysfs handle to /value and direction.
//
// lock must be held.
func (p *Pin) open() error {
	if exportHandle == nil {
		return errors.New("sysfs gpio is not initialized")
	}
	if p.fDirection != nil {
		return nil
	}
	// Ignore the failure unless this is a permission failure. Exporting a pin
	// that is already exported causes a write failure.
	_, err := exportHandle.Write([]byte(fmt.Sprintf("%d\n", p.number)))
	if os.IsPermission(err) {
		return err
	}
	// There's a race condition where the file may be created but udev is still
	// running the script to make it readable to the current user. It's simpler
	// to just loop a little as if /export is accessible, it doesn't make sense
	// that gpioN/value doesn't become accessible eventually.
	timeout := 5 * time.Second
	for start := time.Now(); time.Since(start) < timeout; {
		p.fValue, err = os.OpenFile(p.root+"value", os.O_RDWR, 0600)
		// The virtual file creation is synchronous when writing to /export for
		// udev rule execution is asynchronous.
		if err == nil || !os.IsPermission(err) {
			break
		}
	}
	p.fDirection, err = os.OpenFile(p.root+"direction", os.O_RDWR|os.O_APPEND, 0600)
	if err != nil {
		p.fValue.Close()
		p.fValue = nil
	}
	return err
}

// disableEdge disable the edge detection setting for the pin, if any.
func (p *Pin) disableEdge() error {
	if p.direction != dIn {
		return errors.New("pin wasn't set as input first")
	}
	if p.edges != nil {
		// Drain it if needed. This works because p.edges is not buffered.
		select {
		case <-p.edges:
		default:
		}
		// Only after it is safe to close.
		close(p.edges)
		p.edges = nil
		if _, err := p.fEdge.Seek(0, 0); err != nil {
			return err
		}
		if _, err := p.fEdge.Write([]byte("none")); err != nil {
			return err
		}
	}
	return nil
}

func (p *Pin) edgeLoop(started *sync.WaitGroup) {
	defer p.wg.Done()
	last := p.Read()
	started.Done()
	var b [1]byte
	for {
		for {
			p.lock.Lock()
			c := p.edges
			p.lock.Unlock()
			if c == nil {
				return
			}
			if _, err := p.fValue.Seek(0, 0); err != nil {
				log.Printf("edgeLoop() ended: %v\n", err)
				return
			}
			if nr, err := p.event.wait(p.epollFd); err != nil {
				log.Printf("edgeLoop() ended: %v\n", err)
				return
			} else if nr < 1 {
				continue
			}
			if _, err := p.fValue.Read(b[:]); err != nil {
				log.Printf("edgeLoop() ended: %v\n", err)
				return
			}
			break
		}
		p.lock.Lock()
		c := p.edges
		p.lock.Unlock()
		if c == nil {
			return
		}
		// Make sure to ignore spurious wake up.
		if b[0] == '1' {
			if last != gpio.High {
				c <- gpio.High
				last = gpio.High
			}
		} else {
			if last != gpio.Low {
				c <- gpio.Low
				last = gpio.Low
			}
		}
	}
}

//

var exportHandle io.Writer // handle to /sys/class/gpio/export

const (
	dUnknown = 0
	dIn      = 1
	dOut     = 2
)

func readInt(path string) (int, error) {
	raw, err := ioutil.ReadFile(path)
	if err != nil {
		return 0, err
	}
	if len(raw) == 0 || raw[len(raw)-1] != '\n' {
		return 0, errors.New("invalid value")
	}
	return strconv.Atoi(string(raw[:len(raw)-1]))
}

// driverGPIO implements pio.Driver.
type driverGPIO struct {
}

func (d *driverGPIO) String() string {
	return "sysfs-gpio"
}

func (d *driverGPIO) Type() pio.Type {
	// It intentionally load later than processors.
	return pio.Pins
}

func (d *driverGPIO) Prerequisites() []string {
	return nil
}

// Init initializes GPIO sysfs handling code.
//
// Uses gpio sysfs as described at
// https://www.kernel.org/doc/Documentation/gpio/sysfs.txt
//
// GPIO sysfs is often the only way to do edge triggered interrupts. Doing this
// requires cooperation from a driver in the kernel.
//
// The main drawback of GPIO sysfs is that it doesn't expose internal pull
// resistor and it is much slower than using memory mapped hardware registers.
func (d *driverGPIO) Init() (bool, error) {
	f, err := os.OpenFile("/sys/class/gpio/export", os.O_WRONLY, 0600)
	if err != nil {
		return true, err
	}
	items, err := ioutil.ReadDir("/sys/class/gpio/")
	if err != nil {
		f.Close()
		return true, err
	}
	// There are hosts that use non-continuous pin numbering.
	Pins = map[int]*Pin{}
	for _, item := range items {
		name := item.Name()
		if !strings.HasPrefix(name, "gpiochip") {
			continue
		}
		if err := d.exportGPIOChip("/sys/class/gpio/" + name + "/"); err != nil {
			f.Close()
			return true, err
		}
	}
	exportHandle = f
	return true, nil
}

func (d *driverGPIO) exportGPIOChip(path string) error {
	base, err := readInt(path + "base")
	if err != nil {
		return err
	}
	number, err := readInt(path + "ngpio")
	if err != nil {
		return err
	}
	// TODO(maruel): The chip driver may lie and lists GPIO pins that cannot be
	// exported. The only way to know about it is to export it before opening.
	for i := base; i < base+number; i++ {
		if _, ok := Pins[i]; ok {
			return fmt.Errorf("found two pins with number %d", i)
		}
		p := &Pin{
			number: i,
			name:   fmt.Sprintf("GPIO%d", i),
			root:   fmt.Sprintf("/sys/class/gpio/gpio%d/", i),
		}
		Pins[i] = p
		// Intentionally ignore any error. In that case, a Processor driver already
		// registered the pin.
		_ = gpio.Register(p)
		// We cannot use gpio.MapFunction() since there is no API to determine this.
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 GitHub Inc.

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

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

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

package vttest

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path"
	"syscall"
	"time"

	"github.com/golang/protobuf/proto"
)

// HealthChecker is a callback that impements a service-specific health check
// It must return true if the service at the given `addr` is reachable, false
// otherwerise.
type HealthChecker func(addr string) bool

// VtProcess is a generic handle for a running Vitess process.
// It can be spawned manually or through one of the available
// helper methods.
type VtProcess struct {
	Name         string
	Directory    string
	LogDirectory string
	Binary       string
	ExtraArgs    []string
	Env          []string
	Port         int
	PortGrpc     int
	HealthCheck  HealthChecker

	proc *exec.Cmd
	exit chan error
}

// GetVars returns the JSON contents of the `/debug/vars` endpoint
// of this Vitess-based process. If an error is returned, it probably
// means that the Vitess service has not started successfully.
func (vtp *VtProcess) GetVars() ([]byte, error) {
	url := fmt.Sprintf("http://%s/debug/vars", vtp.Address())
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

// IsHealthy returns whether the monitored Vitess process has started
// successfully.
func (vtp *VtProcess) IsHealthy() bool {
	if vtp.HealthCheck != nil && !vtp.HealthCheck(vtp.Address()) {
		return false
	}
	_, err := vtp.GetVars()
	return err == nil
}

// Address returns the main address for this Vitess process.
// This is usually the main HTTP endpoint for the service.
func (vtp *VtProcess) Address() string {
	return fmt.Sprintf("localhost:%d", vtp.Port)
}

// WaitTerminate attemps to gracefully shutdown the Vitess process by sending
// a SIGTERM, then wait for up to 10s for it to exit. If the process hasn't
// exited cleanly after 10s, a SIGKILL is forced and the corresponding exit
// error is returned to the user
func (vtp *VtProcess) WaitTerminate() error {
	if vtp.proc == nil || vtp.exit == nil {
		return nil
	}

	// Attempt graceful shutdown with SIGTERM first
	vtp.proc.Process.Signal(syscall.SIGTERM)

	select {
	case err := <-vtp.exit:
		vtp.proc = nil
		return err

	case <-time.After(10 * time.Second):
		vtp.proc.Process.Kill()
		vtp.proc = nil
		return <-vtp.exit
	}
}

// WaitStart spawns this Vitess process and waits for it to be up
// and running. The process is considered "up" when it starts serving
// its debug HTTP endpoint -- this means the process was successfully
// started.
// If the process is not healthy after 60s, this method will timeout and
// return an error.
func (vtp *VtProcess) WaitStart() (err error) {
	vtp.proc = exec.Command(
		vtp.Binary,
		"-port", fmt.Sprintf("%d", vtp.Port),
		"-log_dir", vtp.LogDirectory,
		"-alsologtostderr",
	)

	if vtp.PortGrpc != 0 {
		vtp.proc.Args = append(vtp.proc.Args, "-grpc_port")
		vtp.proc.Args = append(vtp.proc.Args, fmt.Sprintf("%d", vtp.PortGrpc))
	}

	vtp.proc.Args = append(vtp.proc.Args, vtp.ExtraArgs...)

	logfile := path.Join(vtp.LogDirectory, fmt.Sprintf("%s.%d.log", vtp.Name, vtp.Port))
	vtp.proc.Stdout, err = os.Create(logfile)
	if err != nil {
		return
	}

	vtp.proc.Env = append(vtp.proc.Env, os.Environ()...)
	vtp.proc.Env = append(vtp.proc.Env, vtp.Env...)

	err = vtp.proc.Start()
	if err != nil {
		return
	}

	vtp.exit = make(chan error)
	go func() {
		vtp.exit <- vtp.proc.Wait()
	}()

	timeout := time.Now().Add(60 * time.Second)
	for time.Now().Before(timeout) {
		if vtp.IsHealthy() {
			return nil
		}

		select {
		case err := <-vtp.exit:
			return fmt.Errorf("process '%s' exited prematurely (err: %s)", vtp.Name, err)
		default:
			time.Sleep(300 * time.Millisecond)
		}
	}

	vtp.proc.Process.Kill()
	return fmt.Errorf("process '%s' timed out after 60s (err: %s)", vtp.Name, <-vtp.exit)
}

// DefaultCharset is the default charset used by MySQL instances
const DefaultCharset = "utf8"

// QueryServerArgs are the default arguments passed to all Vitess query servers
var QueryServerArgs = []string{
	"-queryserver-config-pool-size", "4",
	"-queryserver-config-query-timeout", "300",
	"-queryserver-config-schema-reload-time", "60",
	"-queryserver-config-stream-pool-size", "4",
	"-queryserver-config-transaction-cap", "4",
	"-queryserver-config-transaction-timeout", "300",
	"-queryserver-config-txpool-timeout", "300",
}

// VtcomboProcess returns a VtProcess handle for a local `vtcombo` service,
// configured with the given Config.
// The process must be manually started by calling WaitStart()
func VtcomboProcess(env Environment, args *Config, mysql MySQLManager) *VtProcess {
	vt := &VtProcess{
		Name:         "vtcombo",
		Directory:    env.Directory(),
		LogDirectory: env.LogDirectory(),
		Binary:       env.BinaryPath("vtcombo"),
		Port:         env.PortForProtocol("vtcombo", ""),
		PortGrpc:     env.PortForProtocol("vtcombo", "grpc"),
		HealthCheck:  env.ProcessHealthCheck("vtcombo"),
		Env:          env.EnvVars(),
	}

	user, pass := mysql.Auth()
	socket := mysql.UnixSocket()
	charset := args.Charset
	if charset == "" {
		charset = DefaultCharset
	}

	vt.ExtraArgs = append(vt.ExtraArgs, []string{
		"-db-config-app-charset", charset,
		"-db-config-app-uname", user,
		"-db-config-app-pass", pass,
		"-db-config-dba-charset", charset,
		"-db-config-dba-uname", user,
		"-db-config-dba-pass", pass,
		"-proto_topo", proto.CompactTextString(args.Topology),
		"-mycnf_server_id", "1",
		"-mycnf_socket_file", socket,
		"-normalize_queries",
	}...)

	vt.ExtraArgs = append(vt.ExtraArgs, QueryServerArgs...)
	vt.ExtraArgs = append(vt.ExtraArgs, env.VtcomboArguments()...)

	if args.SchemaDir != "" {
		vt.ExtraArgs = append(vt.ExtraArgs, []string{"-schema_dir", args.SchemaDir}...)
	}
	if args.WebDir != "" {
		vt.ExtraArgs = append(vt.ExtraArgs, []string{"-web_dir", args.WebDir}...)
	}
	if args.WebDir2 != "" {
		vt.ExtraArgs = append(vt.ExtraArgs, []string{"-web_dir2", args.WebDir2}...)
	}

	if socket != "" {
		vt.ExtraArgs = append(vt.ExtraArgs, []string{
			"-db-config-app-unixsocket", socket,
			"-db-config-dba-unixsocket", socket,
		}...)
	} else {
		hostname, p := mysql.Address()
		port := fmt.Sprintf("%d", p)

		vt.ExtraArgs = append(vt.ExtraArgs, []string{
			"-db-config-app-host", hostname,
			"-db-config-app-port", port,
			"-db-config-dba-host", hostname,
			"-db-config-dba-port", port,
		}...)
	}

	vtcomboMysqlPort := env.PortForProtocol("vtcombo_mysql_port", "")
	vt.ExtraArgs = append(vt.ExtraArgs, []string{
		"-mysql_auth_server_impl", "none",
		"-mysql_server_port", fmt.Sprintf("%d", vtcomboMysqlPort),
		"-mysql_server_bind_address", "localhost",
	}...)

	return vt
}
<commit_msg>vttest: Log stderr for Vtcombo processes<commit_after>/*
Copyright 2017 GitHub Inc.

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

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

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

package vttest

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path"
	"syscall"
	"time"

	"github.com/golang/protobuf/proto"
)

// HealthChecker is a callback that impements a service-specific health check
// It must return true if the service at the given `addr` is reachable, false
// otherwerise.
type HealthChecker func(addr string) bool

// VtProcess is a generic handle for a running Vitess process.
// It can be spawned manually or through one of the available
// helper methods.
type VtProcess struct {
	Name         string
	Directory    string
	LogDirectory string
	Binary       string
	ExtraArgs    []string
	Env          []string
	Port         int
	PortGrpc     int
	HealthCheck  HealthChecker

	proc *exec.Cmd
	exit chan error
}

// GetVars returns the JSON contents of the `/debug/vars` endpoint
// of this Vitess-based process. If an error is returned, it probably
// means that the Vitess service has not started successfully.
func (vtp *VtProcess) GetVars() ([]byte, error) {
	url := fmt.Sprintf("http://%s/debug/vars", vtp.Address())
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

// IsHealthy returns whether the monitored Vitess process has started
// successfully.
func (vtp *VtProcess) IsHealthy() bool {
	if vtp.HealthCheck != nil && !vtp.HealthCheck(vtp.Address()) {
		return false
	}
	_, err := vtp.GetVars()
	return err == nil
}

// Address returns the main address for this Vitess process.
// This is usually the main HTTP endpoint for the service.
func (vtp *VtProcess) Address() string {
	return fmt.Sprintf("localhost:%d", vtp.Port)
}

// WaitTerminate attemps to gracefully shutdown the Vitess process by sending
// a SIGTERM, then wait for up to 10s for it to exit. If the process hasn't
// exited cleanly after 10s, a SIGKILL is forced and the corresponding exit
// error is returned to the user
func (vtp *VtProcess) WaitTerminate() error {
	if vtp.proc == nil || vtp.exit == nil {
		return nil
	}

	// Attempt graceful shutdown with SIGTERM first
	vtp.proc.Process.Signal(syscall.SIGTERM)

	select {
	case err := <-vtp.exit:
		vtp.proc = nil
		return err

	case <-time.After(10 * time.Second):
		vtp.proc.Process.Kill()
		vtp.proc = nil
		return <-vtp.exit
	}
}

// WaitStart spawns this Vitess process and waits for it to be up
// and running. The process is considered "up" when it starts serving
// its debug HTTP endpoint -- this means the process was successfully
// started.
// If the process is not healthy after 60s, this method will timeout and
// return an error.
func (vtp *VtProcess) WaitStart() (err error) {
	vtp.proc = exec.Command(
		vtp.Binary,
		"-port", fmt.Sprintf("%d", vtp.Port),
		"-log_dir", vtp.LogDirectory,
		"-alsologtostderr",
	)

	if vtp.PortGrpc != 0 {
		vtp.proc.Args = append(vtp.proc.Args, "-grpc_port")
		vtp.proc.Args = append(vtp.proc.Args, fmt.Sprintf("%d", vtp.PortGrpc))
	}

	vtp.proc.Args = append(vtp.proc.Args, vtp.ExtraArgs...)

	logfile := path.Join(vtp.LogDirectory, fmt.Sprintf("%s.%d.log", vtp.Name, vtp.Port))
	vtp.proc.Stderr, err = os.Create(logfile)
	if err != nil {
		return
	}

	vtp.proc.Env = append(vtp.proc.Env, os.Environ()...)
	vtp.proc.Env = append(vtp.proc.Env, vtp.Env...)

	err = vtp.proc.Start()
	if err != nil {
		return
	}

	vtp.exit = make(chan error)
	go func() {
		vtp.exit <- vtp.proc.Wait()
	}()

	timeout := time.Now().Add(60 * time.Second)
	for time.Now().Before(timeout) {
		if vtp.IsHealthy() {
			return nil
		}

		select {
		case err := <-vtp.exit:
			return fmt.Errorf("process '%s' exited prematurely (err: %s)", vtp.Name, err)
		default:
			time.Sleep(300 * time.Millisecond)
		}
	}

	vtp.proc.Process.Kill()
	return fmt.Errorf("process '%s' timed out after 60s (err: %s)", vtp.Name, <-vtp.exit)
}

// DefaultCharset is the default charset used by MySQL instances
const DefaultCharset = "utf8"

// QueryServerArgs are the default arguments passed to all Vitess query servers
var QueryServerArgs = []string{
	"-queryserver-config-pool-size", "4",
	"-queryserver-config-query-timeout", "300",
	"-queryserver-config-schema-reload-time", "60",
	"-queryserver-config-stream-pool-size", "4",
	"-queryserver-config-transaction-cap", "4",
	"-queryserver-config-transaction-timeout", "300",
	"-queryserver-config-txpool-timeout", "300",
}

// VtcomboProcess returns a VtProcess handle for a local `vtcombo` service,
// configured with the given Config.
// The process must be manually started by calling WaitStart()
func VtcomboProcess(env Environment, args *Config, mysql MySQLManager) *VtProcess {
	vt := &VtProcess{
		Name:         "vtcombo",
		Directory:    env.Directory(),
		LogDirectory: env.LogDirectory(),
		Binary:       env.BinaryPath("vtcombo"),
		Port:         env.PortForProtocol("vtcombo", ""),
		PortGrpc:     env.PortForProtocol("vtcombo", "grpc"),
		HealthCheck:  env.ProcessHealthCheck("vtcombo"),
		Env:          env.EnvVars(),
	}

	user, pass := mysql.Auth()
	socket := mysql.UnixSocket()
	charset := args.Charset
	if charset == "" {
		charset = DefaultCharset
	}

	vt.ExtraArgs = append(vt.ExtraArgs, []string{
		"-db-config-app-charset", charset,
		"-db-config-app-uname", user,
		"-db-config-app-pass", pass,
		"-db-config-dba-charset", charset,
		"-db-config-dba-uname", user,
		"-db-config-dba-pass", pass,
		"-proto_topo", proto.CompactTextString(args.Topology),
		"-mycnf_server_id", "1",
		"-mycnf_socket_file", socket,
		"-normalize_queries",
	}...)

	vt.ExtraArgs = append(vt.ExtraArgs, QueryServerArgs...)
	vt.ExtraArgs = append(vt.ExtraArgs, env.VtcomboArguments()...)

	if args.SchemaDir != "" {
		vt.ExtraArgs = append(vt.ExtraArgs, []string{"-schema_dir", args.SchemaDir}...)
	}
	if args.WebDir != "" {
		vt.ExtraArgs = append(vt.ExtraArgs, []string{"-web_dir", args.WebDir}...)
	}
	if args.WebDir2 != "" {
		vt.ExtraArgs = append(vt.ExtraArgs, []string{"-web_dir2", args.WebDir2}...)
	}

	if socket != "" {
		vt.ExtraArgs = append(vt.ExtraArgs, []string{
			"-db-config-app-unixsocket", socket,
			"-db-config-dba-unixsocket", socket,
		}...)
	} else {
		hostname, p := mysql.Address()
		port := fmt.Sprintf("%d", p)

		vt.ExtraArgs = append(vt.ExtraArgs, []string{
			"-db-config-app-host", hostname,
			"-db-config-app-port", port,
			"-db-config-dba-host", hostname,
			"-db-config-dba-port", port,
		}...)
	}

	vtcomboMysqlPort := env.PortForProtocol("vtcombo_mysql_port", "")
	vt.ExtraArgs = append(vt.ExtraArgs, []string{
		"-mysql_auth_server_impl", "none",
		"-mysql_server_port", fmt.Sprintf("%d", vtcomboMysqlPort),
		"-mysql_server_bind_address", "localhost",
	}...)

	return vt
}
<|endoftext|>