<commit_before>package node

//IMPORT parts ----------------------------------------------------------
import (
	// ggv "code.google.com/p/gographviz"
	"fmt"
	sender "github.com/tgermain/grandRepositorySky/communicator/sender"
	"github.com/tgermain/grandRepositorySky/dht"
	"github.com/tgermain/grandRepositorySky/shared"
	"sync"
	"time"
)

//Const parts -----------------------------------------------------------
const SPACESIZE = 160
const UPDATEPERIOD = time.Minute
const HEARTBEATPERIOD = time.Second * 10
const HEARBEATTIMEOUT = time.Second * 2
const LOOKUPTIMEOUT = time.Second * 2

//Mutex part -------------------------------------------------------
var mutexSucc = &sync.Mutex{}
var mutexPred = &sync.Mutex{}

//Objects parts ---------------------------------------------------------
type DHTnode struct {
	fingers     []*fingerEntry
	successor   *shared.DistantNode
	predecessor *shared.DistantNode
	commLib     *sender.SenderLink
}

type fingerEntry struct {
	IdKey    string
	nodeResp *shared.DistantNode
}

//Method parts ----------------------------------------------------------

func (currentNode *DHTnode) JoinRing(newNode *shared.DistantNode) {
	currentNode.commLib.SendJoinRing(newNode)
}

func (currentNode *DHTnode) AddToRing(newNode *shared.DistantNode) {
	whereToInsert := currentNode.Lookup(newNode.Id)
	if whereToInsert != nil {
		currentNode.commLib.SendUpdateSuccessor(whereToInsert, newNode)
	} else {
		shared.Logger.Error("Add to ring of %s fail due to a lookup timeout", newNode.Id)
	}
}

func (d *DHTnode) LeaveRing() {
	shared.Logger.Notice("Node %s leaving gracefully the ring.", shared.LocalId)
	d.commLib.SendUpdateSuccessor(d.predecessor, d.successor)
	d.commLib.SendUpdatePredecessor(d.successor, d.predecessor)
}

//Tell your actual successor that you are no longer its predecessor
//set your succesor to the new value
//tell to your new successor that you are its predecessor
func (d *DHTnode) UpdateSuccessor(newNode *shared.DistantNode) {
	mutexSucc.Lock()
	defer mutexSucc.Unlock()
	shared.Logger.Notice("update successor with %s", newNode.Id)
	//possible TODO : condition on the origin of the message for this sending ?
	if d.successor.Id != newNode.Id {
		// if d.successor.Id != newNode.Id {
		d.commLib.SendUpdatePredecessor(d.successor, newNode)
		// }

		d.successor = newNode
		d.commLib.SendUpdatePredecessor(newNode, d.ToDistantNode())

	} else {
		shared.Logger.Info("Succesor stable !!")
		d.PrintNodeInfo()
	}
}

func (d *DHTnode) UpdatePredecessor(newNode *shared.DistantNode) {
	mutexPred.Lock()
	defer mutexPred.Unlock()
	shared.Logger.Notice("update predecessor with %s", newNode.Id)
	if d.predecessor.Id != newNode.Id {

		d.predecessor = newNode
		d.commLib.SendUpdateSuccessor(newNode, d.ToDistantNode())
		// d.UpdateFingerTable()

	} else {
		shared.Logger.Info("predecessor stable !!")
		d.PrintNodeInfo()
	}
}

func (currentNode *DHTnode) ToDistantNode() *shared.DistantNode {
	return &shared.DistantNode{
		shared.LocalId,
		shared.LocalIp,
		shared.LocalPort,
	}
}

func (currentNode *DHTnode) IsResponsible(IdToSearch string) bool {
	switch {
	case shared.LocalId == currentNode.GetSuccesor().Id:
		{
			return true
		}
	case dht.Between(shared.LocalId, currentNode.GetSuccesor().Id, IdToSearch):
		{
			return true
		}
	default:
		{
			return false
		}
	}
}

func (currentNode *DHTnode) Lookup(IdToSearch string) *shared.DistantNode {
	shared.Logger.Info("Node [%s] made a lookup to [%s]", shared.LocalId, IdToSearch)
	// currentNode.PrintNodeInfo()
	if currentNode.IsResponsible(IdToSearch) {
		//replace with send
		return currentNode.ToDistantNode()
	} else {
		// fmt.Println("go to the next one")
		//TODO use the fingers table here
		responseChan := currentNode.commLib.SendLookup(currentNode.FindClosestNode(IdToSearch), IdToSearch)
		select {
		case res := <-responseChan:
			return &res
		//case of timeout ?
		case <-time.After(LOOKUPTIMEOUT):
			shared.Logger.Error("Lookup for %s timeout", IdToSearch)
			return nil
		}
	}

}

func (currentNode *DHTnode) FindClosestNode(IdToSearch string) *shared.DistantNode {
	bestFinger := currentNode.GetSuccesor()

	minDistance := dht.Distance([]byte(currentNode.GetSuccesor().Id), []byte(IdToSearch), SPACESIZE)
	// fmt.Println("distance successor " + minDistance.String())
	// var bestIndex int
	for _, v := range currentNode.fingers {
		if v != nil {
			if dht.Between(v.nodeResp.Id, shared.LocalId, IdToSearch) {

				//If the finger lead the node to itself, it's not an optimization
				if v.nodeResp.Id != shared.LocalId {

					//if a member of finger table brought closer than the actual one, we udate the value of minDistance and of the chosen finger
					currentDistance := dht.Distance([]byte(v.nodeResp.Id), []byte(IdToSearch), SPACESIZE)

					// x.cmp(y)
					// -1 if x <  y
					//  0 if x == y
					// +1 if x >  y

					if minDistance.Cmp(currentDistance) == 1 {
						shared.Logger.Notice("Better finger ellected ! Lookup for [%s] ->[%s] instead of [%s]", IdToSearch, v.nodeResp.Id, bestFinger.Id)
						// fmt.Println("Old best distance " + minDistance.String())
						// fmt.Println("New best distance " + currentDistance.String())
						// currentNode.PrintNodeInfo()
						// bestIndex = i
						// v.tmp.PrintNodeInfo()
						minDistance = currentDistance
						bestFinger = v.nodeResp
					}
				}
			}
		}
	}
	// fmt.Printf("From [%s] We have found the bes way to go to [%s] : we go throught finger[%d], [%s]\n", shared.LocalId, IdToSearch, bestIndex, bestFinger.Id)
	return bestFinger
}

func (node *DHTnode) UpdateFingerTable() {
	shared.Logger.Notice("Update finger table")
	// fmt.Printf("****************************************************************Node [%s] : init finger table \n", shared.LocalId)
	for i := 0; i < SPACESIZE; i++ {
		// fmt.Printf("Calculatin fingers [%d]\n", i)
		//TODO make a condition to voId to always calculate the fingerId
		fingerId, _ := dht.CalcFinger([]byte(shared.LocalId), i+1, SPACESIZE)
		responsibleNode := node.Lookup(fingerId)
		if responsibleNode != nil {

					shared.Logger.Info("Update of finger %d with value %s", i, responsibleNode.Id)
			node.fingers[i] = &fingerEntry{fingerId, &shared.DistantNode{responsibleNode.Id, responsibleNode.Ip, responsibleNode.Port}}
		} else {
			shared.Logger.Error("Update of finger %d fail due to a lookup timeout", i)
		}

	}
	// fmt.Println("****************************************************************Fingers table init DONE : ")
}

func (node *DHTnode) PrintRing() {
	daString := ""
	node.PrintNodeName(&daString)
	node.commLib.SendPrintRing(node.GetSuccesor(), &daString)
}

func (node *DHTnode) PrintNodeName(currentString *string) {
	*currentString += fmt.Sprintf("%s\n", shared.LocalId)
}

func (node *DHTnode) PrintNodeInfo() {
	shared.Logger.Info("---------------------------------")
	shared.Logger.Info("Node info")
	shared.Logger.Info("---------------------------------")
	shared.Logger.Info("	Id			%s", shared.LocalId)
	shared.Logger.Info("	Ip			%s", shared.LocalIp)
	shared.Logger.Info("	Port		%s", shared.LocalPort)
	shared.Logger.Info(" 	Succesor	%s", node.successor.Id)
	shared.Logger.Info(" 	Predecesor	%s", node.predecessor.Id)
	// fmt.Println("  Fingers table :")
	// fmt.Println("  ---------------------------------")
	// fmt.Println("  Index		Idkey			IdNode ")
	// for i, v := range node.fingers {
	// 	if v != nil {
	// 		fmt.Printf("  %d 		%s					%s\n", i, v.IdKey, v.IdResp)
	// 	}
	// }
	shared.Logger.Info("---------------------------------")
}

// func (node *DHTnode) gimmeGraph(g *ggv.Graph, firstNodeId *string) string {
// 	if &shared.LocalId == firstNodeId {
// 		return g.String()
// 	} else {
// 		if g == nil {
// 			g = ggv.NewGraph()
// 			g.SetName("DHTRing")
// 			g.SetDir(true)
// 		}
// 		if firstNodeId == nil {
// 			firstNodeId = &shared.LocalId
// 		}
// 		g.AddNode(g.Name, shared.LocalId, nil)
// 		g.AddNode(g.Name, node.successor.Id, nil)
// 		g.AddNode(g.Name, node.predecessor.Id, nil)
// 		// g.AddEdge(shared.LocalId, node.successor.Id, true, map[string]string{
// 		// 	"label": "succ",
// 		// })
// 		// g.AddEdge(shared.LocalId, node.predecessor.Id, true, map[string]string{
// 		// 	"label": "pred",
// 		// })

// 		for i, v := range node.fingers {
// 			g.AddEdge(shared.LocalId, v.IdKey, true, map[string]string{
// 				"label":         fmt.Sprintf("\"%s.%d\"", shared.LocalId, i),
// 				"label_scheme":  "3",
// 				"decorate":      "true",
// 				"labelfontsize": "5.0",
// 				"labelfloat":    "true",
// 				"color":         "blue",
// 			})
// 		}

// 		//recursion !
// 		//TODO successor.tmp not accessible anymore later
// 		return node.successor.tmp.gimmeGraph(g, firstNodeId)

// 	}
// }

func (d *DHTnode) GetSuccesor() *shared.DistantNode {
	mutexSucc.Lock()
	defer mutexSucc.Unlock()
	temp := *d.successor
	return &temp
}

func (d *DHTnode) GetPredecessor() *shared.DistantNode {
	mutexPred.Lock()
	defer mutexPred.Unlock()
	temp := *d.predecessor
	return &temp
}

func (d *DHTnode) GetFingerTable() []*fingerEntry {
	temp := d.fingers
	return temp
}
func (d *DHTnode) updateFingersRoutine() {
	shared.Logger.Notice("Starting update fingers table routing")
	for {
		time.Sleep(UPDATEPERIOD)
		shared.Logger.Notice("Auto updating finger table of node %s", shared.LocalId)
		d.UpdateFingerTable()
	}
}

func (d *DHTnode) heartBeatRoutine() {
	shared.Logger.Info("Starting heartBeat routing")
	for {
		time.Sleep(HEARTBEATPERIOD)
		d.sendHeartBeat(d.GetSuccesor())
	}
}

func (d *DHTnode) sendHeartBeat(destination *shared.DistantNode) {

	responseChan := d.commLib.SendHeartBeat(d.GetSuccesor())
	select {
	case <-responseChan:
		{
			//Everything this node is alive. Do nothing more
			shared.Logger.Notice("%s still alive", destination.Id)
		}
	//case of timeout ?
	case <-time.After(HEARBEATTIMEOUT):
		shared.Logger.Error("heartBeat to %s timeout", destination.Id)
		//DANGER
	}
}

//other functions parts --------------------------------------------------------
//Create the node with it's communication interface
//Does not start to liten for message
func MakeNode() (*DHTnode, *sender.SenderLink) {
	daComInterface := sender.NewSenderLink()
	daNode := DHTnode{
		fingers: make([]*fingerEntry, SPACESIZE),
		commLib: daComInterface,
	}
	mySelf := daNode.ToDistantNode()
	daNode.successor = mySelf

	daNode.predecessor = mySelf
	// initialization of fingers table is done while adding the node to the ring
	// The fingers table of the first node of a ring is initialized when a second node is added to the ring

	//Initialize the finger table with each finger pointing to the node frehly created itself
	shared.Logger.Info("New node [%.5s] createde", shared.LocalId)
	go daNode.heartBeatRoutine()
	go daNode.updateFingersRoutine()

	return &daNode, daComInterface
}
<commit_msg>update finger table when update successor<commit_after>package node

//IMPORT parts ----------------------------------------------------------
import (
	// ggv "code.google.com/p/gographviz"
	"fmt"
	sender "github.com/tgermain/grandRepositorySky/communicator/sender"
	"github.com/tgermain/grandRepositorySky/dht"
	"github.com/tgermain/grandRepositorySky/shared"
	"sync"
	"time"
)

//Const parts -----------------------------------------------------------
const SPACESIZE = 160
const UPDATEPERIOD = time.Minute
const HEARTBEATPERIOD = time.Second * 10
const HEARBEATTIMEOUT = time.Second * 2
const LOOKUPTIMEOUT = time.Second * 2

//Mutex part -------------------------------------------------------
var mutexSucc = &sync.Mutex{}
var mutexPred = &sync.Mutex{}

//Objects parts ---------------------------------------------------------
type DHTnode struct {
	fingers     []*fingerEntry
	successor   *shared.DistantNode
	predecessor *shared.DistantNode
	commLib     *sender.SenderLink
}

type fingerEntry struct {
	IdKey    string
	nodeResp *shared.DistantNode
}

//Method parts ----------------------------------------------------------

func (currentNode *DHTnode) JoinRing(newNode *shared.DistantNode) {
	currentNode.commLib.SendJoinRing(newNode)
}

func (currentNode *DHTnode) AddToRing(newNode *shared.DistantNode) {
	whereToInsert := currentNode.Lookup(newNode.Id)
	if whereToInsert != nil {
		currentNode.commLib.SendUpdateSuccessor(whereToInsert, newNode)
	} else {
		shared.Logger.Error("Add to ring of %s fail due to a lookup timeout", newNode.Id)
	}
}

func (d *DHTnode) LeaveRing() {
	shared.Logger.Notice("Node %s leaving gracefully the ring.", shared.LocalId)
	d.commLib.SendUpdateSuccessor(d.predecessor, d.successor)
	d.commLib.SendUpdatePredecessor(d.successor, d.predecessor)
}

//Tell your actual successor that you are no longer its predecessor
//set your succesor to the new value
//tell to your new successor that you are its predecessor
func (d *DHTnode) UpdateSuccessor(newNode *shared.DistantNode) {
	mutexSucc.Lock()
	defer mutexSucc.Unlock()
	shared.Logger.Notice("update successor with %s", newNode.Id)
	//possible TODO : condition on the origin of the message for this sending ?
	if d.successor.Id != newNode.Id {
		// if d.successor.Id != newNode.Id {
		d.commLib.SendUpdatePredecessor(d.successor, newNode)
		// }

		d.successor = newNode
		d.commLib.SendUpdatePredecessor(newNode, d.ToDistantNode())

		go d.UpdateFingerTable()
	} else {
		shared.Logger.Info("Succesor stable !!")
	}
}

func (d *DHTnode) UpdatePredecessor(newNode *shared.DistantNode) {
	mutexPred.Lock()
	defer mutexPred.Unlock()
	shared.Logger.Notice("update predecessor with %s", newNode.Id)
	if d.predecessor.Id != newNode.Id {

		d.predecessor = newNode
		d.commLib.SendUpdateSuccessor(newNode, d.ToDistantNode())
		// d.UpdateFingerTable()

	} else {
		shared.Logger.Info("predecessor stable !!")
		d.PrintNodeInfo()
	}
}

func (currentNode *DHTnode) ToDistantNode() *shared.DistantNode {
	return &shared.DistantNode{
		shared.LocalId,
		shared.LocalIp,
		shared.LocalPort,
	}
}

func (currentNode *DHTnode) IsResponsible(IdToSearch string) bool {
	switch {
	case shared.LocalId == currentNode.GetSuccesor().Id:
		{
			return true
		}
	case dht.Between(shared.LocalId, currentNode.GetSuccesor().Id, IdToSearch):
		{
			return true
		}
	default:
		{
			return false
		}
	}
}

func (currentNode *DHTnode) Lookup(IdToSearch string) *shared.DistantNode {
	shared.Logger.Info("Node [%s] made a lookup to [%s]", shared.LocalId, IdToSearch)
	// currentNode.PrintNodeInfo()
	if currentNode.IsResponsible(IdToSearch) {
		//replace with send
		return currentNode.ToDistantNode()
	} else {
		// fmt.Println("go to the next one")
		//TODO use the fingers table here
		responseChan := currentNode.commLib.SendLookup(currentNode.FindClosestNode(IdToSearch), IdToSearch)
		select {
		case res := <-responseChan:
			return &res
		//case of timeout ?
		case <-time.After(LOOKUPTIMEOUT):
			shared.Logger.Error("Lookup for %s timeout", IdToSearch)
			return nil
		}
	}

}

func (currentNode *DHTnode) FindClosestNode(IdToSearch string) *shared.DistantNode {
	bestFinger := currentNode.GetSuccesor()

	minDistance := dht.Distance([]byte(currentNode.GetSuccesor().Id), []byte(IdToSearch), SPACESIZE)
	// fmt.Println("distance successor " + minDistance.String())
	// var bestIndex int
	for _, v := range currentNode.fingers {
		if v != nil {
			if dht.Between(v.nodeResp.Id, shared.LocalId, IdToSearch) {

				//If the finger lead the node to itself, it's not an optimization
				if v.nodeResp.Id != shared.LocalId {

					//if a member of finger table brought closer than the actual one, we udate the value of minDistance and of the chosen finger
					currentDistance := dht.Distance([]byte(v.nodeResp.Id), []byte(IdToSearch), SPACESIZE)

					// x.cmp(y)
					// -1 if x <  y
					//  0 if x == y
					// +1 if x >  y

					if minDistance.Cmp(currentDistance) == 1 {
						shared.Logger.Notice("Better finger ellected ! Lookup for [%s] ->[%s] instead of [%s]", IdToSearch, v.nodeResp.Id, bestFinger.Id)
						// fmt.Println("Old best distance " + minDistance.String())
						// fmt.Println("New best distance " + currentDistance.String())
						// currentNode.PrintNodeInfo()
						// bestIndex = i
						// v.tmp.PrintNodeInfo()
						minDistance = currentDistance
						bestFinger = v.nodeResp
					}
				}
			}
		}
	}
	// fmt.Printf("From [%s] We have found the bes way to go to [%s] : we go throught finger[%d], [%s]\n", shared.LocalId, IdToSearch, bestIndex, bestFinger.Id)
	return bestFinger
}

func (node *DHTnode) UpdateFingerTable() {
	shared.Logger.Notice("Update finger table")
	// fmt.Printf("****************************************************************Node [%s] : init finger table \n", shared.LocalId)
	for i := 0; i < SPACESIZE; i++ {
		// fmt.Printf("Calculatin fingers [%d]\n", i)
		//TODO make a condition to voId to always calculate the fingerId
		fingerId, _ := dht.CalcFinger([]byte(shared.LocalId), i+1, SPACESIZE)
		responsibleNode := node.Lookup(fingerId)
		if responsibleNode != nil {

					shared.Logger.Info("Update of finger %d with value %s", i, responsibleNode.Id)
			node.fingers[i] = &fingerEntry{fingerId, &shared.DistantNode{responsibleNode.Id, responsibleNode.Ip, responsibleNode.Port}}
		} else {
			shared.Logger.Error("Update of finger %d fail due to a lookup timeout", i)
		}

	}
	// fmt.Println("****************************************************************Fingers table init DONE : ")
}

func (node *DHTnode) PrintRing() {
	daString := ""
	node.PrintNodeName(&daString)
	node.commLib.SendPrintRing(node.GetSuccesor(), &daString)
}

func (node *DHTnode) PrintNodeName(currentString *string) {
	*currentString += fmt.Sprintf("%s\n", shared.LocalId)
}

func (node *DHTnode) PrintNodeInfo() {
	shared.Logger.Info("---------------------------------")
	shared.Logger.Info("Node info")
	shared.Logger.Info("---------------------------------")
	shared.Logger.Info("	Id			%s", shared.LocalId)
	shared.Logger.Info("	Ip			%s", shared.LocalIp)
	shared.Logger.Info("	Port		%s", shared.LocalPort)
	shared.Logger.Info(" 	Succesor	%s", node.successor.Id)
	shared.Logger.Info(" 	Predecesor	%s", node.predecessor.Id)
	// fmt.Println("  Fingers table :")
	// fmt.Println("  ---------------------------------")
	// fmt.Println("  Index		Idkey			IdNode ")
	// for i, v := range node.fingers {
	// 	if v != nil {
	// 		fmt.Printf("  %d 		%s					%s\n", i, v.IdKey, v.IdResp)
	// 	}
	// }
	shared.Logger.Info("---------------------------------")
}

// func (node *DHTnode) gimmeGraph(g *ggv.Graph, firstNodeId *string) string {
// 	if &shared.LocalId == firstNodeId {
// 		return g.String()
// 	} else {
// 		if g == nil {
// 			g = ggv.NewGraph()
// 			g.SetName("DHTRing")
// 			g.SetDir(true)
// 		}
// 		if firstNodeId == nil {
// 			firstNodeId = &shared.LocalId
// 		}
// 		g.AddNode(g.Name, shared.LocalId, nil)
// 		g.AddNode(g.Name, node.successor.Id, nil)
// 		g.AddNode(g.Name, node.predecessor.Id, nil)
// 		// g.AddEdge(shared.LocalId, node.successor.Id, true, map[string]string{
// 		// 	"label": "succ",
// 		// })
// 		// g.AddEdge(shared.LocalId, node.predecessor.Id, true, map[string]string{
// 		// 	"label": "pred",
// 		// })

// 		for i, v := range node.fingers {
// 			g.AddEdge(shared.LocalId, v.IdKey, true, map[string]string{
// 				"label":         fmt.Sprintf("\"%s.%d\"", shared.LocalId, i),
// 				"label_scheme":  "3",
// 				"decorate":      "true",
// 				"labelfontsize": "5.0",
// 				"labelfloat":    "true",
// 				"color":         "blue",
// 			})
// 		}

// 		//recursion !
// 		//TODO successor.tmp not accessible anymore later
// 		return node.successor.tmp.gimmeGraph(g, firstNodeId)

// 	}
// }

func (d *DHTnode) GetSuccesor() *shared.DistantNode {
	mutexSucc.Lock()
	defer mutexSucc.Unlock()
	temp := *d.successor
	return &temp
}

func (d *DHTnode) GetPredecessor() *shared.DistantNode {
	mutexPred.Lock()
	defer mutexPred.Unlock()
	temp := *d.predecessor
	return &temp
}

func (d *DHTnode) GetFingerTable() []*fingerEntry {
	temp := d.fingers
	return temp
}
func (d *DHTnode) updateFingersRoutine() {
	shared.Logger.Notice("Starting update fingers table routing")
	for {
		time.Sleep(UPDATEPERIOD)
		shared.Logger.Notice("Auto updating finger table of node %s", shared.LocalId)
		d.UpdateFingerTable()
	}
}

func (d *DHTnode) heartBeatRoutine() {
	shared.Logger.Info("Starting heartBeat routing")
	for {
		time.Sleep(HEARTBEATPERIOD)
		d.sendHeartBeat(d.GetSuccesor())
	}
}

func (d *DHTnode) sendHeartBeat(destination *shared.DistantNode) {

	responseChan := d.commLib.SendHeartBeat(d.GetSuccesor())
	select {
	case <-responseChan:
		{
			//Everything this node is alive. Do nothing more
			shared.Logger.Notice("%s still alive", destination.Id)
		}
	//case of timeout ?
	case <-time.After(HEARBEATTIMEOUT):
		shared.Logger.Error("heartBeat to %s timeout", destination.Id)
		//DANGER
	}
}

//other functions parts --------------------------------------------------------
//Create the node with it's communication interface
//Does not start to liten for message
func MakeNode() (*DHTnode, *sender.SenderLink) {
	daComInterface := sender.NewSenderLink()
	daNode := DHTnode{
		fingers: make([]*fingerEntry, SPACESIZE),
		commLib: daComInterface,
	}
	mySelf := daNode.ToDistantNode()
	daNode.successor = mySelf

	daNode.predecessor = mySelf
	// initialization of fingers table is done while adding the node to the ring
	// The fingers table of the first node of a ring is initialized when a second node is added to the ring

	//Initialize the finger table with each finger pointing to the node frehly created itself
	shared.Logger.Info("New node [%.5s] createde", shared.LocalId)
	go daNode.heartBeatRoutine()
	go daNode.updateFingersRoutine()

	return &daNode, daComInterface
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	"gopkg.in/src-d/go-kallax.v1/generator/cli/kallax/cmd"

	"gopkg.in/urfave/cli.v1"
)

const version = "1.2.11"

func main() {
	newApp().Run(os.Args)
}

func newApp() *cli.App {
	app := cli.NewApp()
	app.Name = "kallax"
	app.Version = version
	app.Usage = "generate kallax models"
	app.Flags = cmd.Generate.Flags
	app.Action = cmd.Generate.Action
	app.Commands = cli.Commands{
		cmd.Generate,
		cmd.Migrate,
	}

	return app
}
<commit_msg>Update cmd.go<commit_after>package main

import (
	"os"

	"gopkg.in/src-d/go-kallax.v1/generator/cli/kallax/cmd"

	"gopkg.in/urfave/cli.v1"
)

const version = "1.2.12"

func main() {
	newApp().Run(os.Args)
}

func newApp() *cli.App {
	app := cli.NewApp()
	app.Name = "kallax"
	app.Version = version
	app.Usage = "generate kallax models"
	app.Flags = cmd.Generate.Flags
	app.Action = cmd.Generate.Action
	app.Commands = cli.Commands{
		cmd.Generate,
		cmd.Migrate,
	}

	return app
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"

	"github.com/flynn/flynn/discoverd/client"
)

type services struct {
	clientCmd
	onlyOne *bool
}

func (cmd *services) Name() string {
	return "services"
}

func (cmd *services) DefineFlags(fs *flag.FlagSet) {
	cmd.onlyOne = fs.Bool("1", false, "only show one service")
}

func (cmd *services) Run(fs *flag.FlagSet) {
	cmd.InitClient(false)
	services, err := cmd.client.Services(fs.Arg(0), discoverd.DefaultTimeout)
	if err != nil {
		log.Fatal(err)
	}
	if *cmd.onlyOne {
		if len(services) > 0 {
			fmt.Println(services[0].Addr)
		}
		return
	}
	for _, service := range services {
		fmt.Println(service.Addr)
	}
}
<commit_msg>sdutil: "services" command requires a service name<commit_after>package main

import (
	"flag"
	"fmt"
	"log"

	"github.com/flynn/flynn/discoverd/client"
)

type services struct {
	clientCmd
	onlyOne *bool
}

func (cmd *services) Name() string {
	return "services"
}

func (cmd *services) DefineFlags(fs *flag.FlagSet) {
	cmd.onlyOne = fs.Bool("1", false, "only show one service")
}

func (cmd *services) Run(fs *flag.FlagSet) {
	if fs.Arg(0) == "" {
		log.Fatal("missing service name argument")
	}
	cmd.InitClient(false)
	services, err := cmd.client.Services(fs.Arg(0), discoverd.DefaultTimeout)
	if err != nil {
		log.Fatal(err)
	}
	if *cmd.onlyOne {
		if len(services) > 0 {
			fmt.Println(services[0].Addr)
		}
		return
	}
	for _, service := range services {
		fmt.Println(service.Addr)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"log"
	"net/url"
	"os"
	"strings"
	"time"
	"unicode"

	maas "github.com/juju/gomaasapi"
)

const (
	// defaultFilter specifies the default filter to use when none is specified
	defaultFilter = `{
	  "hosts" : {
	    "include" : [ ".*" ],
		"exclude" : []
	  },
	  "zones" : {
	    "include" : ["default"],
		"exclude" : []
           }
	}`
	defaultMapping = "{}"
	PROVISION_URL  = "PROVISION_URL"
	PROVISION_TTL  = "PROVISION_TTL"
	DEFAULT_TTL    = "30m"
)

var apiKey = flag.String("apikey", "", "key with which to access MAAS server")
var maasURL = flag.String("maas", "http://localhost/MAAS", "url over which to access MAAS")
var apiVersion = flag.String("apiVersion", "1.0", "version of the API to access")
var queryPeriod = flag.String("period", "15s", "frequency the MAAS service is polled for node states")
var preview = flag.Bool("preview", false, "displays the action that would be taken, but does not do the action, in this mode the nodes are processed only once")
var mappings = flag.String("mappings", "{}", "the mac to name mappings")
var always = flag.Bool("always-rename", true, "attempt to rename at every stage of workflow")
var verbose = flag.Bool("verbose", false, "display verbose logging")
var filterSpec = flag.String("filter", strings.Map(func(r rune) rune {
	if unicode.IsSpace(r) {
		return -1
	}
	return r
}, defaultFilter), "constrain by hostname what will be automated")

// checkError if the given err is not nil, then fatally log the message, else
// return false.
func checkError(err error, message string, v ...interface{}) bool {
	if err != nil {
		log.Fatalf("[error] "+message, v)
	}
	return false
}

// checkWarn if the given err is not nil, then log the message as a warning and
// return true, else return false.
func checkWarn(err error, message string, v ...interface{}) bool {
	if err != nil {
		log.Printf("[warn] "+message, v)
		return true
	}
	return false
}

// fetchNodes do a HTTP GET to the MAAS server to query all the nodes
func fetchNodes(client *maas.MAASObject) ([]MaasNode, error) {
	nodeListing := client.GetSubObject("nodes")
	listNodeObjects, err := nodeListing.CallGet("list", url.Values{})
	if checkWarn(err, "unable to get the list of all nodes: %s", err) {
		return nil, err
	}
	listNodes, err := listNodeObjects.GetArray()
	if checkWarn(err, "unable to get the node objects for the list: %s", err) {
		return nil, err
	}

	var nodes = make([]MaasNode, len(listNodes))
	for index, nodeObj := range listNodes {
		node, err := nodeObj.GetMAASObject()
		if !checkWarn(err, "unable to retrieve object for node: %s", err) {
			nodes[index] = MaasNode{node}
		}
	}
	return nodes, nil
}

func main() {

	flag.Parse()

	options := ProcessingOptions{
		Preview:      *preview,
		Verbose:      *verbose,
		AlwaysRename: *always,
		ProvTracker:  NewTracker(),
		ProvisionURL: os.Getenv(PROVISION_URL),
	}

	var ttl string
	if ttl = os.Getenv(PROVISION_TTL); ttl == "" {
		ttl = "30m"
	}

	var err error
	options.ProvisionTTL, err = time.ParseDuration(ttl)
	if err != nil {
		log.Printf("[warn] unable to parse specified duration of '%s', defaulting to '%s'",
			ttl, DEFAULT_TTL)
	}

	// Determine the filter, this can either be specified on the the command
	// line as a value or a file reference. If none is specified the default
	// will be used
	if len(*filterSpec) > 0 {
		if (*filterSpec)[0] == '@' {
			name := os.ExpandEnv((*filterSpec)[1:])
			file, err := os.OpenFile(name, os.O_RDONLY, 0)
			checkError(err, "[error] unable to open file '%s' to load the filter : %s", name, err)
			decoder := json.NewDecoder(file)
			err = decoder.Decode(&options.Filter)
			checkError(err, "[error] unable to parse filter configuration from file '%s' : %s", name, err)
		} else {
			err := json.Unmarshal([]byte(*filterSpec), &options.Filter)
			checkError(err, "[error] unable to parse filter specification: '%s' : %s", *filterSpec, err)
		}
	} else {
		err := json.Unmarshal([]byte(defaultFilter), &options.Filter)
		checkError(err, "[error] unable to parse default filter specificiation: '%s' : %s", defaultFilter, err)
	}

	// Determine the mac to name mapping, this can either be specified on the the command
	// line as a value or a file reference. If none is specified the default
	// will be used
	if len(*mappings) > 0 {
		if (*mappings)[0] == '@' {
			name := os.ExpandEnv((*mappings)[1:])
			file, err := os.OpenFile(name, os.O_RDONLY, 0)
			checkError(err, "[error] unable to open file '%s' to load the mac name mapping : %s", name, err)
			decoder := json.NewDecoder(file)
			err = decoder.Decode(&options.Mappings)
			checkError(err, "[error] unable to parse filter configuration from file '%s' : %s", name, err)
		} else {
			err := json.Unmarshal([]byte(*mappings), &options.Mappings)
			checkError(err, "[error] unable to parse mac name mapping: '%s' : %s", *mappings, err)
		}
	} else {
		err := json.Unmarshal([]byte(defaultMapping), &options.Mappings)
		checkError(err, "[error] unable to parse default mac name mappings: '%s' : %s", defaultMapping, err)
	}

	// Verify the specified period for queries can be converted into a Go duration
	period, err := time.ParseDuration(*queryPeriod)
	checkError(err, "[error] unable to parse specified query period duration: '%s': %s", queryPeriod, err)

	authClient, err := maas.NewAuthenticatedClient(*maasURL, *apiKey, *apiVersion)
	if err != nil {
		checkError(err, "[error] Unable to use specified client key, '%s', to authenticate to the MAAS server: %s", *apiKey, err)
	}

	// Create an object through which we will communicate with MAAS
	client := maas.NewMAAS(*authClient)

	// This utility essentially polls the MAAS server for node state and
	// process the node to the next state. This is done by kicking off the
	// process every specified duration. This means that the first processing of
	// nodes will have "period" in the future. This is really not the behavior
	// we want, we really want, do it now, and then do the next one in "period".
	// So, the code does one now.
	nodes, _ := fetchNodes(client)
	ProcessAll(client, nodes, options)

	if !(*preview) {
		// Create a ticker and fetch and process the nodes every "period"
		ticker := time.NewTicker(period)
		for t := range ticker.C {
			log.Printf("[info] query server at %s", t)
			nodes, _ := fetchNodes(client)
			ProcessAll(client, nodes, options)
		}
	}
}
<commit_msg>implemented fallback to TTL wait time for provisining as well as display configuraiton on start<commit_after>package main

import (
	"encoding/json"
	"flag"
	"log"
	"net/url"
	"os"
	"strings"
	"time"
	"unicode"

	maas "github.com/juju/gomaasapi"
)

const (
	// defaultFilter specifies the default filter to use when none is specified
	defaultFilter = `{
	  "hosts" : {
	    "include" : [ ".*" ],
		"exclude" : []
	  },
	  "zones" : {
	    "include" : ["default"],
		"exclude" : []
           }
	}`
	defaultMapping = "{}"
	PROVISION_URL  = "PROVISION_URL"
	PROVISION_TTL  = "PROVISION_TTL"
	DEFAULT_TTL    = "30m"
)

var apiKey = flag.String("apikey", "", "key with which to access MAAS server")
var maasURL = flag.String("maas", "http://localhost/MAAS", "url over which to access MAAS")
var apiVersion = flag.String("apiVersion", "1.0", "version of the API to access")
var queryPeriod = flag.String("period", "15s", "frequency the MAAS service is polled for node states")
var preview = flag.Bool("preview", false, "displays the action that would be taken, but does not do the action, in this mode the nodes are processed only once")
var mappings = flag.String("mappings", "{}", "the mac to name mappings")
var always = flag.Bool("always-rename", true, "attempt to rename at every stage of workflow")
var verbose = flag.Bool("verbose", false, "display verbose logging")
var filterSpec = flag.String("filter", strings.Map(func(r rune) rune {
	if unicode.IsSpace(r) {
		return -1
	}
	return r
}, defaultFilter), "constrain by hostname what will be automated")

// checkError if the given err is not nil, then fatally log the message, else
// return false.
func checkError(err error, message string, v ...interface{}) bool {
	if err != nil {
		log.Fatalf("[error] "+message, v)
	}
	return false
}

// checkWarn if the given err is not nil, then log the message as a warning and
// return true, else return false.
func checkWarn(err error, message string, v ...interface{}) bool {
	if err != nil {
		log.Printf("[warn] "+message, v)
		return true
	}
	return false
}

// fetchNodes do a HTTP GET to the MAAS server to query all the nodes
func fetchNodes(client *maas.MAASObject) ([]MaasNode, error) {
	nodeListing := client.GetSubObject("nodes")
	listNodeObjects, err := nodeListing.CallGet("list", url.Values{})
	if checkWarn(err, "unable to get the list of all nodes: %s", err) {
		return nil, err
	}
	listNodes, err := listNodeObjects.GetArray()
	if checkWarn(err, "unable to get the node objects for the list: %s", err) {
		return nil, err
	}

	var nodes = make([]MaasNode, len(listNodes))
	for index, nodeObj := range listNodes {
		node, err := nodeObj.GetMAASObject()
		if !checkWarn(err, "unable to retrieve object for node: %s", err) {
			nodes[index] = MaasNode{node}
		}
	}
	return nodes, nil
}

func main() {

	flag.Parse()

	options := ProcessingOptions{
		Preview:      *preview,
		Verbose:      *verbose,
		AlwaysRename: *always,
		ProvTracker:  NewTracker(),
		ProvisionURL: os.Getenv(PROVISION_URL),
	}

	var ttl string
	if ttl = os.Getenv(PROVISION_TTL); ttl == "" {
		ttl = "30m"
	}

	var err error
	options.ProvisionTTL, err = time.ParseDuration(ttl)
	if err != nil {
		log.Printf("[warn] unable to parse specified duration of '%s', defaulting to '%s'",
			ttl, DEFAULT_TTL)
		options.ProvisionTTL, err = time.ParseDuration("30m")
		checkError(err, "[error] unable to parse default TTL duration of '30m' : %s", err)
	}

	// Determine the filter, this can either be specified on the the command
	// line as a value or a file reference. If none is specified the default
	// will be used
	if len(*filterSpec) > 0 {
		if (*filterSpec)[0] == '@' {
			name := os.ExpandEnv((*filterSpec)[1:])
			file, err := os.OpenFile(name, os.O_RDONLY, 0)
			checkError(err, "[error] unable to open file '%s' to load the filter : %s", name, err)
			decoder := json.NewDecoder(file)
			err = decoder.Decode(&options.Filter)
			checkError(err, "[error] unable to parse filter configuration from file '%s' : %s", name, err)
		} else {
			err := json.Unmarshal([]byte(*filterSpec), &options.Filter)
			checkError(err, "[error] unable to parse filter specification: '%s' : %s", *filterSpec, err)
		}
	} else {
		err := json.Unmarshal([]byte(defaultFilter), &options.Filter)
		checkError(err, "[error] unable to parse default filter specificiation: '%s' : %s", defaultFilter, err)
	}

	// Determine the mac to name mapping, this can either be specified on the the command
	// line as a value or a file reference. If none is specified the default
	// will be used
	if len(*mappings) > 0 {
		if (*mappings)[0] == '@' {
			name := os.ExpandEnv((*mappings)[1:])
			file, err := os.OpenFile(name, os.O_RDONLY, 0)
			checkError(err, "[error] unable to open file '%s' to load the mac name mapping : %s", name, err)
			decoder := json.NewDecoder(file)
			err = decoder.Decode(&options.Mappings)
			checkError(err, "[error] unable to parse filter configuration from file '%s' : %s", name, err)
		} else {
			err := json.Unmarshal([]byte(*mappings), &options.Mappings)
			checkError(err, "[error] unable to parse mac name mapping: '%s' : %s", *mappings, err)
		}
	} else {
		err := json.Unmarshal([]byte(defaultMapping), &options.Mappings)
		checkError(err, "[error] unable to parse default mac name mappings: '%s' : %s", defaultMapping, err)
	}

	// Verify the specified period for queries can be converted into a Go duration
	period, err := time.ParseDuration(*queryPeriod)
	checkError(err, "[error] unable to parse specified query period duration: '%s': %s", queryPeriod, err)

	log.Printf(`Configuration:
	    MAAS URL:            %s
	    MAAS API Version:    %s
	    MAAS Query Interval: %s
	    Node Filtter:        %s
	    Node Name Mappings:  %s
	    Preview:             %v
	    Verbose:             %v
	    Always Rename:       %v
	    Provision URL:       %s `,
		*maasURL, *apiVersion, *queryPeriod, *filterSpec, *mappings, options.Preview,
		options.Verbose, options.AlwaysRename, options.ProvisionURL)

	authClient, err := maas.NewAuthenticatedClient(*maasURL, *apiKey, *apiVersion)
	if err != nil {
		checkError(err, "[error] Unable to use specified client key, '%s', to authenticate to the MAAS server: %s", *apiKey, err)
	}

	// Create an object through which we will communicate with MAAS
	client := maas.NewMAAS(*authClient)

	// This utility essentially polls the MAAS server for node state and
	// process the node to the next state. This is done by kicking off the
	// process every specified duration. This means that the first processing of
	// nodes will have "period" in the future. This is really not the behavior
	// we want, we really want, do it now, and then do the next one in "period".
	// So, the code does one now.
	nodes, _ := fetchNodes(client)
	ProcessAll(client, nodes, options)

	if !(*preview) {
		// Create a ticker and fetch and process the nodes every "period"
		ticker := time.NewTicker(period)
		for t := range ticker.C {
			log.Printf("[info] query server at %s", t)
			nodes, _ := fetchNodes(client)
			ProcessAll(client, nodes, options)
		}
	}
}
<|endoftext|>


<commit_before>package portping

import (
	"testing"
	"net"
	"strings"
	"time"
	"strconv"
)

const (
	testHost = "localhost"
	knownNonexistentHost = "nonexistent.janosgyerik.com"
	defaultTimeout = 5 * time.Second
	testNetwork = "tcp"
)

var testPort = findKnownAvailablePort()

func findKnownAvailablePort() string {
	tcpa, err := net.ResolveTCPAddr("tcp", ":0")
	if err != nil {
		panic(err)
	}
	ln, err := net.ListenTCP("tcp", tcpa)
	if err != nil {
		panic(err)
	}
	defer ln.Close()

	local, ok := ln.Addr().(*net.TCPAddr)
	if !ok {
		panic("Unable to convert Addr to TCPAddr")
	}

	return strconv.Itoa(local.Port)
}

func acceptN(t*testing.T, host, port string, count int) {
	ready := make(chan bool)
	go func() {
		ln, err := net.Listen(testNetwork, net.JoinHostPort(host, port))
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()

		ready <- true

		for i := 0; i < count; i++ {
			conn, err := ln.Accept()
			if err != nil {
				t.Fatal(err)
			}
			conn.Close()
		}
	}()
	<-ready
}

func assertPingResult(t*testing.T, host, port string, expectSuccess bool, patterns ...string) {
	addr := net.JoinHostPort(host, port)
	err := Ping(testNetwork, addr, defaultTimeout)
	t.Logf("port ping %s -> %v", addr, err)

	if err != nil {
		if expectSuccess {
			t.Errorf("ping to %s failed; expected success", addr)
		} else {
			assertErrorContains(t, err, patterns...)
		}
	} else {
		if !expectSuccess {
			t.Errorf("ping to %s success; expected failure", addr)
		}
	}
}

func assertErrorContains(t*testing.T, err error, patterns ...string) {
	result := err.Error()
	foundMatch := false
	for _, pattern := range patterns {
		if strings.Contains(result, pattern) {
			foundMatch = true
			break
		}
	}
	if !foundMatch {
		t.Errorf("got '%s'; expected to contain one of '%s'", result, patterns)
	}
}

func assertPingFailure(t*testing.T, host, port string, patterns ...string) {
	assertPingResult(t, host, port, false, patterns...)
}

func assertPingNSuccessCount(t*testing.T, host, port string, pingCount int, expectedSuccessCount int) {
	c := make(chan error)
	addr := net.JoinHostPort(host, port)
	go PingN(testNetwork, addr, defaultTimeout, pingCount, c)

	failureCount := 0
	for i := 0; i < pingCount; i++ {
		err := <-c
		t.Logf("port ping %s [%d] -> %v", addr, i + 1, err)

		if err != nil {
			failureCount++
		}
	}

	successCount := pingCount - failureCount
	if successCount != expectedSuccessCount {
		t.Errorf("expected %d successful pings, but got %d", expectedSuccessCount, successCount)
	}
}

func Test_ping_open_port(t*testing.T) {
	acceptN(t, testHost, testPort, 1)

	assertPingResult(t, testHost, testPort, true)

	// for sanity: acceptN should have shut down already
	assertPingFailure(t, testHost, testPort, "connection refused")
}

func Test_ping_unopen_port(t*testing.T) {
	assertPingFailure(t, testHost, testPort, "connection refused")
}

func Test_ping_nonexistent_host(t*testing.T) {
	assertPingFailure(t, knownNonexistentHost, testPort, "no such host")
}

func Test_ping_negative_port(t*testing.T) {
	assertPingFailure(t, testHost, "-1", "invalid port", "unknown port")
}

func Test_ping_too_high_port(t*testing.T) {
	assertPingFailure(t, testHost, "123456", "invalid port", "unknown port")
}

func Test_ping5_all_success(t*testing.T) {
	pingCount := 3
	acceptN(t, testHost, testPort, pingCount)

	assertPingNSuccessCount(t, testHost, testPort, pingCount, pingCount)
}

func Test_ping5_all_fail(t*testing.T) {
	pingCount := 5
	successCount := 0
	assertPingNSuccessCount(t, testHost, testPort, pingCount, successCount)
}

func Test_ping5_partial_success(t*testing.T) {
	successCount := 3
	acceptN(t, testHost, testPort, successCount)

	pingCount := 5
	assertPingNSuccessCount(t, testHost, testPort, pingCount, successCount)
}
<commit_msg>if accept is still shutting down, error might be different<commit_after>package portping

import (
	"testing"
	"net"
	"strings"
	"time"
	"strconv"
)

const (
	testHost = "localhost"
	knownNonexistentHost = "nonexistent.janosgyerik.com"
	defaultTimeout = 5 * time.Second
	testNetwork = "tcp"
)

var testPort = findKnownAvailablePort()

func findKnownAvailablePort() string {
	tcpa, err := net.ResolveTCPAddr("tcp", ":0")
	if err != nil {
		panic(err)
	}
	ln, err := net.ListenTCP("tcp", tcpa)
	if err != nil {
		panic(err)
	}
	defer ln.Close()

	local, ok := ln.Addr().(*net.TCPAddr)
	if !ok {
		panic("Unable to convert Addr to TCPAddr")
	}

	return strconv.Itoa(local.Port)
}

func acceptN(t*testing.T, host, port string, count int) {
	ready := make(chan bool)
	go func() {
		ln, err := net.Listen(testNetwork, net.JoinHostPort(host, port))
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()

		ready <- true

		for i := 0; i < count; i++ {
			conn, err := ln.Accept()
			if err != nil {
				t.Fatal(err)
			}
			conn.Close()
		}
	}()
	<-ready
}

func assertPingResult(t*testing.T, host, port string, expectSuccess bool, patterns ...string) {
	addr := net.JoinHostPort(host, port)
	err := Ping(testNetwork, addr, defaultTimeout)
	t.Logf("port ping %s -> %v", addr, err)

	if err != nil {
		if expectSuccess {
			t.Errorf("ping to %s failed; expected success", addr)
		} else {
			assertErrorContains(t, err, patterns...)
		}
	} else {
		if !expectSuccess {
			t.Errorf("ping to %s success; expected failure", addr)
		}
	}
}

func assertErrorContains(t*testing.T, err error, patterns ...string) {
	result := err.Error()
	foundMatch := false
	for _, pattern := range patterns {
		if strings.Contains(result, pattern) {
			foundMatch = true
			break
		}
	}
	if !foundMatch {
		t.Errorf("got '%s'; expected to contain one of '%s'", result, patterns)
	}
}

func assertPingFailure(t*testing.T, host, port string, patterns ...string) {
	assertPingResult(t, host, port, false, patterns...)
}

func assertPingNSuccessCount(t*testing.T, host, port string, pingCount int, expectedSuccessCount int) {
	c := make(chan error)
	addr := net.JoinHostPort(host, port)
	go PingN(testNetwork, addr, defaultTimeout, pingCount, c)

	failureCount := 0
	for i := 0; i < pingCount; i++ {
		err := <-c
		t.Logf("port ping %s [%d] -> %v", addr, i + 1, err)

		if err != nil {
			failureCount++
		}
	}

	successCount := pingCount - failureCount
	if successCount != expectedSuccessCount {
		t.Errorf("expected %d successful pings, but got %d", expectedSuccessCount, successCount)
	}
}

func Test_ping_open_port(t*testing.T) {
	acceptN(t, testHost, testPort, 1)

	assertPingResult(t, testHost, testPort, true)

	// for sanity: acceptN should have shut down already
	// note: "connection reset" is seen in Linux, I suspect acceptN hasn't shut down yet
	assertPingFailure(t, testHost, testPort, "connection refused", "connection reset")
}

func Test_ping_unopen_port(t*testing.T) {
	assertPingFailure(t, testHost, testPort, "connection refused")
}

func Test_ping_nonexistent_host(t*testing.T) {
	assertPingFailure(t, knownNonexistentHost, testPort, "no such host")
}

func Test_ping_negative_port(t*testing.T) {
	assertPingFailure(t, testHost, "-1", "invalid port", "unknown port")
}

func Test_ping_too_high_port(t*testing.T) {
	assertPingFailure(t, testHost, "123456", "invalid port", "unknown port")
}

func Test_ping5_all_success(t*testing.T) {
	pingCount := 3
	acceptN(t, testHost, testPort, pingCount)

	assertPingNSuccessCount(t, testHost, testPort, pingCount, pingCount)
}

func Test_ping5_all_fail(t*testing.T) {
	pingCount := 5
	successCount := 0
	assertPingNSuccessCount(t, testHost, testPort, pingCount, successCount)
}

func Test_ping5_partial_success(t*testing.T) {
	successCount := 3
	acceptN(t, testHost, testPort, successCount)

	pingCount := 5
	assertPingNSuccessCount(t, testHost, testPort, pingCount, successCount)
}
<|endoftext|>


<commit_before>package portping

import (
	"testing"
	"net"
	"strings"
	"time"
)

const (
	testHost = "localhost"
	testPort = "4269"
	knownNonexistentHost = "nonexistent.janosgyerik.com"
	defaultTimeout = 5 * time.Second
	testNetwork = "tcp"
)

func acceptN(t*testing.T, host, port string, count int) {
	ready := make(chan bool)
	go func() {
		ln, err := net.Listen(testNetwork, net.JoinHostPort(host, port))
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()

		ready <- true

		for i := 0; i < count; i++ {
			conn, err := ln.Accept()
			if err != nil {
				t.Fatal(err)
			}
			conn.Close()
		}
	}()
	<-ready
}

func assertPingResult(t*testing.T, host, port string, expected bool, patterns ...string) {
	addr := net.JoinHostPort(host, port)
	err := Ping(testNetwork, addr, defaultTimeout)
	t.Logf("port ping %s -> %v", addr, err)

	actual := err == nil

	if actual != expected {
		var openOrClosed string
		if expected {
			openOrClosed = "open"
		} else {
			openOrClosed = "closed"
		}
		t.Errorf("%s should be %s", addr, openOrClosed)
	}

	if err != nil {
		assertErrorContains(t, err, patterns...)
	}
}

func assertPingSuccess(t*testing.T, host, port string) {
	assertPingResult(t, host, port, true)
}

func assertPingFailure(t*testing.T, host, port string, patterns ...string) {
	assertPingResult(t, host, port, false, patterns...)
}

func assertPingNSuccessCount(t*testing.T, host, port string, pingCount int, expectedSuccessCount int) {
	c := make(chan error)
	addr := net.JoinHostPort(host, port)
	go PingN(testNetwork, addr, defaultTimeout, pingCount, c)

	failureCount := 0
	for i := 0; i < pingCount; i++ {
		err := <-c
		t.Logf("port ping %s [%d] -> %v", addr, i + 1, err)

		if err != nil {
			failureCount++
		}
	}

	successCount := pingCount - failureCount
	if expectedSuccessCount != successCount {
		t.Errorf("expected %d successful pings, but got only %d", expectedSuccessCount, successCount)
	}
}

func Test_ping_open_port(t*testing.T) {
	acceptN(t, testHost, testPort, 1)

	assertPingSuccess(t, testHost, testPort)

	// for sanity: acceptN should have shut down already
	assertPingFailure(t, testHost, testPort, "connection refused")
}

func Test_ping_unopen_port(t*testing.T) {
	assertPingFailure(t, testHost, testPort, "connection refused")
}

func Test_ping_nonexistent_host(t*testing.T) {
	assertPingFailure(t, knownNonexistentHost, testPort, "no such host")
}

func Test_ping_negative_port(t*testing.T) {
	assertPingFailure(t, testHost, "-1", "invalid port", "unknown port")
}

func Test_ping_too_high_port(t*testing.T) {
	assertPingFailure(t, testHost, "123456", "invalid port", "unknown port")
}

func Test_ping5_all_success(t*testing.T) {
	pingCount := 3
	acceptN(t, testHost, testPort, pingCount)

	assertPingNSuccessCount(t, testHost, testPort, pingCount, pingCount)
}

func Test_ping5_all_fail(t*testing.T) {
	pingCount := 5
	successCount := 0
	assertPingNSuccessCount(t, testHost, testPort, pingCount, successCount)
}

func Test_ping5_partial_success(t*testing.T) {
	successCount := 3
	acceptN(t, testHost, testPort, successCount)

	pingCount := 5
	assertPingNSuccessCount(t, testHost, testPort, pingCount, successCount)
}

func assertErrorContains(t*testing.T, err error, patterns ...string) {
	result := err.Error()
	foundMatch := false
	for _, pattern := range patterns {
		if strings.Contains(result, pattern) {
			foundMatch = true
			break
		}
	}
	if !foundMatch {
		t.Errorf("got '%s'; expected to contain one of '%s'", result, patterns)
	}
}
<commit_msg>inlined method that was used only once<commit_after>package portping

import (
	"testing"
	"net"
	"strings"
	"time"
)

const (
	testHost = "localhost"
	testPort = "4269"
	knownNonexistentHost = "nonexistent.janosgyerik.com"
	defaultTimeout = 5 * time.Second
	testNetwork = "tcp"
)

func acceptN(t*testing.T, host, port string, count int) {
	ready := make(chan bool)
	go func() {
		ln, err := net.Listen(testNetwork, net.JoinHostPort(host, port))
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()

		ready <- true

		for i := 0; i < count; i++ {
			conn, err := ln.Accept()
			if err != nil {
				t.Fatal(err)
			}
			conn.Close()
		}
	}()
	<-ready
}

func assertPingResult(t*testing.T, host, port string, expected bool, patterns ...string) {
	addr := net.JoinHostPort(host, port)
	err := Ping(testNetwork, addr, defaultTimeout)
	t.Logf("port ping %s -> %v", addr, err)

	actual := err == nil

	if actual != expected {
		var openOrClosed string
		if expected {
			openOrClosed = "open"
		} else {
			openOrClosed = "closed"
		}
		t.Errorf("%s should be %s", addr, openOrClosed)
	}

	if err != nil {
		assertErrorContains(t, err, patterns...)
	}
}

func assertPingFailure(t*testing.T, host, port string, patterns ...string) {
	assertPingResult(t, host, port, false, patterns...)
}

func assertPingNSuccessCount(t*testing.T, host, port string, pingCount int, expectedSuccessCount int) {
	c := make(chan error)
	addr := net.JoinHostPort(host, port)
	go PingN(testNetwork, addr, defaultTimeout, pingCount, c)

	failureCount := 0
	for i := 0; i < pingCount; i++ {
		err := <-c
		t.Logf("port ping %s [%d] -> %v", addr, i + 1, err)

		if err != nil {
			failureCount++
		}
	}

	successCount := pingCount - failureCount
	if expectedSuccessCount != successCount {
		t.Errorf("expected %d successful pings, but got only %d", expectedSuccessCount, successCount)
	}
}

func Test_ping_open_port(t*testing.T) {
	acceptN(t, testHost, testPort, 1)

	assertPingResult(t, testHost, testPort, true)

	// for sanity: acceptN should have shut down already
	assertPingFailure(t, testHost, testPort, "connection refused")
}

func Test_ping_unopen_port(t*testing.T) {
	assertPingFailure(t, testHost, testPort, "connection refused")
}

func Test_ping_nonexistent_host(t*testing.T) {
	assertPingFailure(t, knownNonexistentHost, testPort, "no such host")
}

func Test_ping_negative_port(t*testing.T) {
	assertPingFailure(t, testHost, "-1", "invalid port", "unknown port")
}

func Test_ping_too_high_port(t*testing.T) {
	assertPingFailure(t, testHost, "123456", "invalid port", "unknown port")
}

func Test_ping5_all_success(t*testing.T) {
	pingCount := 3
	acceptN(t, testHost, testPort, pingCount)

	assertPingNSuccessCount(t, testHost, testPort, pingCount, pingCount)
}

func Test_ping5_all_fail(t*testing.T) {
	pingCount := 5
	successCount := 0
	assertPingNSuccessCount(t, testHost, testPort, pingCount, successCount)
}

func Test_ping5_partial_success(t*testing.T) {
	successCount := 3
	acceptN(t, testHost, testPort, successCount)

	pingCount := 5
	assertPingNSuccessCount(t, testHost, testPort, pingCount, successCount)
}

func assertErrorContains(t*testing.T, err error, patterns ...string) {
	result := err.Error()
	foundMatch := false
	for _, pattern := range patterns {
		if strings.Contains(result, pattern) {
			foundMatch = true
			break
		}
	}
	if !foundMatch {
		t.Errorf("got '%s'; expected to contain one of '%s'", result, patterns)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package client

import (
	"fmt"

	"golang.org/x/net/context"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
)

// CmdDeviceAdd is the 'device add' command.  It is used for
// device provisioning on the provisioner/device X/C1.
type CmdDeviceAdd struct {
	libkb.Contextified
}

const cmdDevAddDesc = `When you are adding a new device to your account and you have an
existing device, you will be prompted to use this command on your
existing device to authorize the new device.`

// NewCmdDeviceAdd creates a new cli.Command.
func NewCmdDeviceAdd(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:        "add",
		Usage:       "Authorize a new device",
		Description: cmdDevAddDesc,
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdDeviceAdd{Contextified: libkb.NewContextified(g)}, "add", c)
		},
	}
}

// RunClient runs the command in client/server mode.
func (c *CmdDeviceAdd) Run() error {
	var err error
	cli, err := GetDeviceClient(c.G())
	if err != nil {
		return err
	}
	protocols := []rpc.Protocol{
		NewProvisionUIProtocol(c.G(), libkb.KexRoleProvisioner),
		NewSecretUIProtocol(c.G()),
	}
	if err := RegisterProtocols(protocols); err != nil {
		return err
	}

	return cli.DeviceAdd(context.TODO(), 0)
}

// ParseArgv gets the secret phrase from the command args.
func (c *CmdDeviceAdd) ParseArgv(ctx *cli.Context) error {
	if len(ctx.Args()) != 0 {
		return fmt.Errorf("device add takes zero arguments")
	}
	return nil
}

// GetUsage says what this command needs to operate.
func (c *CmdDeviceAdd) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:    true,
		KbKeyring: true,
		API:       true,
	}
}
<commit_msg>Add helpful text to `device add`<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package client

import (
	"fmt"

	"golang.org/x/net/context"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
)

// CmdDeviceAdd is the 'device add' command.  It is used for
// device provisioning on the provisioner/device X/C1.
type CmdDeviceAdd struct {
	libkb.Contextified
}

const cmdDevAddDesc = `When you are adding a new device to your account and you have an
existing device, you will be prompted to use this command on your
existing device to authorize the new device.`

// NewCmdDeviceAdd creates a new cli.Command.
func NewCmdDeviceAdd(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:        "add",
		Usage:       "Authorize a new device",
		Description: cmdDevAddDesc,
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdDeviceAdd{Contextified: libkb.NewContextified(g)}, "add", c)
		},
	}
}

// RunClient runs the command in client/server mode.
func (c *CmdDeviceAdd) Run() error {
	dui := c.G().UI.GetDumbOutputUI()
	dui.Printf("Starting `device add`...\n\n")
	dui.Printf("(Please note that you should run `device add` on a computer that is\n")
	dui.Printf("already registered with Keybase)\n")

	cli, err := GetDeviceClient(c.G())
	if err != nil {
		return err
	}
	protocols := []rpc.Protocol{
		NewProvisionUIProtocol(c.G(), libkb.KexRoleProvisioner),
		NewSecretUIProtocol(c.G()),
	}
	if err := RegisterProtocols(protocols); err != nil {
		return err
	}

	if err := cli.DeviceAdd(context.TODO(), 0); err != nil {
		if lsErr, ok := err.(libkb.LoginStateTimeoutError); ok {
			c.G().Log.Debug("caught a LoginStateTimeoutError in `device add` command: %s", lsErr)
			c.G().Log.Debug("providing hopefully helpful terminal output...")

			dui.Printf("\n\nSorry, but it looks like there is another login or device provisioning\n")
			dui.Printf("task currently running.\n\n")
			dui.Printf("We only run one at a time to ensure the device is provisioned correctly.\n\n")
			dui.Printf("(Note that this often happens when you run `device add` on a new\n")
			dui.Printf("computer while it is being provisioned. You need to run it on an\n")
			dui.Printf("existing computer that is already reqistered with Keybase.)\n")
			return nil
		}
		return err
	}

	return nil
}

// ParseArgv gets the secret phrase from the command args.
func (c *CmdDeviceAdd) ParseArgv(ctx *cli.Context) error {
	if len(ctx.Args()) != 0 {
		return fmt.Errorf("device add takes zero arguments")
	}
	return nil
}

// GetUsage says what this command needs to operate.
func (c *CmdDeviceAdd) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:    true,
		KbKeyring: true,
		API:       true,
	}
}
<|endoftext|>


<commit_before>package common

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

	"github.com/eris-ltd/common/go/log" // so we can flush logs on exit/ifexit
	"github.com/mitchellh/go-homedir"
)

var (
	// Convenience Directories
	GoPath  = os.Getenv("GOPATH")
	ErisLtd = path.Join(GoPath, "src", "github.com", "eris-ltd")
	ErisGH  = "https://github.com/eris-ltd/"
	// usr, _   = user.Current() // error?!
	ErisRoot          = ResolveErisRoot()
	ErisContainerRoot = "/home/eris/.eris" // XXX: this is used as root in the `eris/base` image

	// Major Directories
	AppsPath           = path.Join(ErisRoot, "apps") // previously "dapps"
	ActionsPath        = path.Join(ErisRoot, "actions")
	ChainsPath         = path.Join(ErisRoot, "chains") // previously "blockchains"
	DataContainersPath = path.Join(ErisRoot, "data")
	KeysPath           = path.Join(ErisRoot, "keys")
	LanguagesPath      = path.Join(ErisRoot, "languages")
	ServicesPath       = path.Join(ErisRoot, "services")
	ScratchPath        = path.Join(ErisRoot, "scratch")

	//Deprecated Directories
	BlockchainsPath = path.Join(ErisRoot, "blockchains")
	DappsPath       = path.Join(ErisRoot, "dapps")

	// Keys
	KeysDataPath = path.Join(KeysPath, "data")
	KeyNamesPath = path.Join(KeysPath, "names")

	// Scratch Directories (globally coordinated)
	EpmScratchPath  = path.Join(ScratchPath, "epm")
	LllcScratchPath = path.Join(ScratchPath, "lllc")
	SolcScratchPath = path.Join(ScratchPath, "sol")
	SerpScratchPath = path.Join(ScratchPath, "ser")

	// Blockchains stuff
	HEAD = path.Join(ChainsPath, "HEAD")
	Refs = path.Join(ChainsPath, "refs")
)

var MajorDirs = []string{
	ErisRoot, ActionsPath, ChainsPath, DataContainersPath, AppsPath, KeysPath, LanguagesPath, ServicesPath, KeysDataPath, KeyNamesPath, ScratchPath, EpmScratchPath, LllcScratchPath, SolcScratchPath, SerpScratchPath,
}

//eris update checks if old dirs exist & migrates them
var DirsToMigrate = map[string]string{
	BlockchainsPath: ChainsPath,
	DappsPath:       AppsPath,
}

//---------------------------------------------
// user and process

func Usr() string {
	u, _ := homedir.Dir()
	return u
}

func Exit(err error) {
	status := 0
	if err != nil {
		log.Flush()
		fmt.Println(err)
		status = 1
	}
	os.Exit(status)
}

func IfExit(err error) {
	if err != nil {
		log.Flush()
		fmt.Println(err)
		os.Exit(1)
	}
}

// user and process
//---------------------------------------------------------------------------
// filesystem

func AbsolutePath(Datadir string, filename string) string {
	if path.IsAbs(filename) {
		return filename
	}
	return path.Join(Datadir, filename)
}

func InitDataDir(Datadir string) error {
	if _, err := os.Stat(Datadir); err != nil {
		if os.IsNotExist(err) {
			if err := os.MkdirAll(Datadir, 0777); err != nil {
				return err
			}
		}
	}
	return nil
}

func ResolveErisRoot() string {
	var eris string
	if os.Getenv("ERIS") != "" {
		eris = os.Getenv("ERIS")
	} else {
		if runtime.GOOS == "windows" {
			home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
			if home == "" {
				home = os.Getenv("USERPROFILE")
			}
			eris = path.Join(home, ".eris")
		} else {
			eris = path.Join(Usr(), ".eris")
		}
	}
	return eris
}

// Create the default eris tree
func InitErisDir() (err error) {
	for _, d := range MajorDirs {
		err := InitDataDir(d)
		if err != nil {
			return err
		}
	}
	if _, err = os.Stat(HEAD); err != nil {
		_, err = os.Create(HEAD)
	}
	return
}

func ClearDir(dir string) error {
	fs, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}
	for _, f := range fs {
		n := f.Name()
		if f.IsDir() {
			if err := os.RemoveAll(path.Join(dir, f.Name())); err != nil {
				return err
			}
		} else {
			if err := os.Remove(path.Join(dir, n)); err != nil {
				return err
			}
		}
	}
	return nil
}

func Copy(src, dst string) error {
	f, err := os.Stat(src)
	if err != nil {
		return err
	}
	if f.IsDir() {
		tmpDir, err := ioutil.TempDir(os.TempDir(), "eris_copy")
		if err != nil {
			return err
		}
		if err := copyDir(src, tmpDir); err != nil {
			return err
		}
		if err := copyDir(tmpDir, dst); err != nil {
			return err
		}
		// fi, err := os.Stat(src)
		// if err := os.MkdirAll(dst, fi.Mode()); err != nil {
		// 	return err
		// }
		// return os.Rename(tmpDir, dst)
		return nil
	}
	return copyFile(src, dst)
}

// assumes we've done our checking
func copyDir(src, dst string) error {
	fi, err := os.Stat(src)
	if err := os.MkdirAll(dst, fi.Mode()); err != nil {
		return err
	}
	fs, err := ioutil.ReadDir(src)
	if err != nil {
		return err
	}

	for _, f := range fs {
		s := path.Join(src, f.Name())
		d := path.Join(dst, f.Name())
		if f.IsDir() {
			if err := copyDir(s, d); err != nil {
				return err
			}
		} else {
			if err := copyFile(s, d); err != nil {
				return err
			}
		}
	}
	return nil
}

// common golang, really?
func copyFile(src, dst string) error {
	r, err := os.Open(src)
	if err != nil {
		return err
	}
	defer r.Close()

	w, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer w.Close()

	_, err = io.Copy(w, r)
	if err != nil {
		return err
	}
	return nil
}

func WriteFile(data, path string) error {
	if err := os.MkdirAll(filepath.Dir(path), 0775); err != nil {
		return err
	}
	writer, err := os.Create(filepath.Join(path))
	defer writer.Close()
	if err != nil {
		return err
	}
	writer.Write([]byte(data))
	return nil
}

// filesystem
//-------------------------------------------------------
// open text editors

func Editor(file string) error {
	editr := os.Getenv("EDITOR")
	if strings.Contains(editr, "/") {
		editr = path.Base(editr)
	}
	switch editr {
	case "", "vim", "vi":
		return vi(file)
	case "emacs":
		return emacs(file)
	default:
		return editor(file)
	}
	// return fmt.Errorf("Unknown editor %s", editr)
}

func emacs(file string) error {
	cmd := exec.Command("emacs", file)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func vi(file string) error {
	cmd := exec.Command("vim", file)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func editor(file string) error {
	cmd := exec.Command(os.Getenv("EDITOR"), file)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}
<commit_msg>add DefaultChainDir<commit_after>package common

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

	"github.com/eris-ltd/common/go/log" // so we can flush logs on exit/ifexit
	"github.com/mitchellh/go-homedir"
)

var (
	// Convenience Directories
	GoPath  = os.Getenv("GOPATH")
	ErisLtd = path.Join(GoPath, "src", "github.com", "eris-ltd")
	ErisGH  = "https://github.com/eris-ltd/"
	// usr, _   = user.Current() // error?!
	ErisRoot          = ResolveErisRoot()
	ErisContainerRoot = "/home/eris/.eris" // XXX: this is used as root in the `eris/base` image

	// Major Directories
	AppsPath           = path.Join(ErisRoot, "apps") // previously "dapps"
	ActionsPath        = path.Join(ErisRoot, "actions")
	ChainsPath         = path.Join(ErisRoot, "chains") // previously "blockchains"
	DataContainersPath = path.Join(ErisRoot, "data")
	KeysPath           = path.Join(ErisRoot, "keys")
	LanguagesPath      = path.Join(ErisRoot, "languages")
	ServicesPath       = path.Join(ErisRoot, "services")
	ScratchPath        = path.Join(ErisRoot, "scratch")

	//Deprecated Directories
	BlockchainsPath = path.Join(ErisRoot, "blockchains")
	DappsPath       = path.Join(ErisRoot, "dapps")

	// Keys
	KeysDataPath = path.Join(KeysPath, "data")
	KeyNamesPath = path.Join(KeysPath, "names")

	// Scratch Directories (globally coordinated)
	EpmScratchPath  = path.Join(ScratchPath, "epm")
	LllcScratchPath = path.Join(ScratchPath, "lllc")
	SolcScratchPath = path.Join(ScratchPath, "sol")
	SerpScratchPath = path.Join(ScratchPath, "ser")

	// Blockchains stuff
	HEAD            = path.Join(ChainsPath, "HEAD")
	Refs            = path.Join(ChainsPath, "refs")
	DefaultChainDir = path.Join(ChainsPath, "default")
)

var MajorDirs = []string{
	ErisRoot, ActionsPath, ChainsPath, DefaultChainDir, DataContainersPath, AppsPath, KeysPath, LanguagesPath, ServicesPath, KeysDataPath, KeyNamesPath, ScratchPath, EpmScratchPath, LllcScratchPath, SolcScratchPath, SerpScratchPath,
}

//eris update checks if old dirs exist & migrates them
var DirsToMigrate = map[string]string{
	BlockchainsPath: ChainsPath,
	DappsPath:       AppsPath,
}

//---------------------------------------------
// user and process

func Usr() string {
	u, _ := homedir.Dir()
	return u
}

func Exit(err error) {
	status := 0
	if err != nil {
		log.Flush()
		fmt.Println(err)
		status = 1
	}
	os.Exit(status)
}

func IfExit(err error) {
	if err != nil {
		log.Flush()
		fmt.Println(err)
		os.Exit(1)
	}
}

// user and process
//---------------------------------------------------------------------------
// filesystem

func AbsolutePath(Datadir string, filename string) string {
	if path.IsAbs(filename) {
		return filename
	}
	return path.Join(Datadir, filename)
}

func InitDataDir(Datadir string) error {
	if _, err := os.Stat(Datadir); err != nil {
		if os.IsNotExist(err) {
			if err := os.MkdirAll(Datadir, 0777); err != nil {
				return err
			}
		}
	}
	return nil
}

func ResolveErisRoot() string {
	var eris string
	if os.Getenv("ERIS") != "" {
		eris = os.Getenv("ERIS")
	} else {
		if runtime.GOOS == "windows" {
			home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
			if home == "" {
				home = os.Getenv("USERPROFILE")
			}
			eris = path.Join(home, ".eris")
		} else {
			eris = path.Join(Usr(), ".eris")
		}
	}
	return eris
}

// Create the default eris tree
func InitErisDir() (err error) {
	for _, d := range MajorDirs {
		err := InitDataDir(d)
		if err != nil {
			return err
		}
	}
	if _, err = os.Stat(HEAD); err != nil {
		_, err = os.Create(HEAD)
	}
	return
}

func ClearDir(dir string) error {
	fs, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}
	for _, f := range fs {
		n := f.Name()
		if f.IsDir() {
			if err := os.RemoveAll(path.Join(dir, f.Name())); err != nil {
				return err
			}
		} else {
			if err := os.Remove(path.Join(dir, n)); err != nil {
				return err
			}
		}
	}
	return nil
}

func Copy(src, dst string) error {
	f, err := os.Stat(src)
	if err != nil {
		return err
	}
	if f.IsDir() {
		tmpDir, err := ioutil.TempDir(os.TempDir(), "eris_copy")
		if err != nil {
			return err
		}
		if err := copyDir(src, tmpDir); err != nil {
			return err
		}
		if err := copyDir(tmpDir, dst); err != nil {
			return err
		}
		// fi, err := os.Stat(src)
		// if err := os.MkdirAll(dst, fi.Mode()); err != nil {
		// 	return err
		// }
		// return os.Rename(tmpDir, dst)
		return nil
	}
	return copyFile(src, dst)
}

// assumes we've done our checking
func copyDir(src, dst string) error {
	fi, err := os.Stat(src)
	if err := os.MkdirAll(dst, fi.Mode()); err != nil {
		return err
	}
	fs, err := ioutil.ReadDir(src)
	if err != nil {
		return err
	}

	for _, f := range fs {
		s := path.Join(src, f.Name())
		d := path.Join(dst, f.Name())
		if f.IsDir() {
			if err := copyDir(s, d); err != nil {
				return err
			}
		} else {
			if err := copyFile(s, d); err != nil {
				return err
			}
		}
	}
	return nil
}

// common golang, really?
func copyFile(src, dst string) error {
	r, err := os.Open(src)
	if err != nil {
		return err
	}
	defer r.Close()

	w, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer w.Close()

	_, err = io.Copy(w, r)
	if err != nil {
		return err
	}
	return nil
}

func WriteFile(data, path string) error {
	if err := os.MkdirAll(filepath.Dir(path), 0775); err != nil {
		return err
	}
	writer, err := os.Create(filepath.Join(path))
	defer writer.Close()
	if err != nil {
		return err
	}
	writer.Write([]byte(data))
	return nil
}

// filesystem
//-------------------------------------------------------
// open text editors

func Editor(file string) error {
	editr := os.Getenv("EDITOR")
	if strings.Contains(editr, "/") {
		editr = path.Base(editr)
	}
	switch editr {
	case "", "vim", "vi":
		return vi(file)
	case "emacs":
		return emacs(file)
	default:
		return editor(file)
	}
	// return fmt.Errorf("Unknown editor %s", editr)
}

func emacs(file string) error {
	cmd := exec.Command("emacs", file)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func vi(file string) error {
	cmd := exec.Command("vim", file)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func editor(file string) error {
	cmd := exec.Command(os.Getenv("EDITOR"), file)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}
<|endoftext|>


<commit_before>// Copyright 2018 ETH Zurich
//
// 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.

// Simple show paths application for SCION.
package main

import (
	"flag"
	"fmt"
	"net"
	"os"
	"time"

	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/common"
	"github.com/scionproto/scion/go/lib/log"
	"github.com/scionproto/scion/go/lib/sciond"
	"github.com/scionproto/scion/go/lib/scmp"
	"github.com/scionproto/scion/go/lib/snet"
	"github.com/scionproto/scion/go/lib/sock/reliable"
	"github.com/scionproto/scion/go/lib/spath"
)

var (
	dstIAStr     = flag.String("dstIA", "", "Destination IA address: ISD-AS")
	srcIAStr     = flag.String("srcIA", "", "Source IA address: ISD-AS")
	sciondPath   = flag.String("sciond", "", "SCIOND socket path")
	timeout      = flag.Duration("timeout", 5*time.Second, "Timeout in seconds")
	maxPaths     = flag.Int("maxpaths", 10, "Maximum number of paths")
	sciondFromIA = flag.Bool("sciondFromIA", false, "SCIOND socket path from IA address:ISD-AS")
	expiration   = flag.Bool("expiration", false, "Show path expiration timestamps")
	refresh      = flag.Bool("refresh", false, "Set refresh flag for SCIOND path request")
	status       = flag.Bool("p", false, "Probe the paths and print out the statuses")
	Local        snet.Addr
)

var (
	dstIA addr.IA
	srcIA addr.IA
)

func main() {
	var err error

	flag.Var((*snet.Addr)(&Local), "local", "Local address to use for health checks")

	log.AddLogConsFlags()
	validateFlags()

	sd := sciond.NewService(*sciondPath)
	sdConn, err := sd.ConnectTimeout(*timeout)
	if err != nil {
		LogFatal("Failed to connect to SCIOND: %v\n", err)
	}
	reply, err := sdConn.Paths(dstIA, srcIA, uint16(*maxPaths),
		sciond.PathReqFlags{Refresh: *refresh})
	if err != nil {
		LogFatal("Failed to retrieve paths from SCIOND: %v\n", err)
	}
	if reply.ErrorCode != sciond.ErrorOk {
		LogFatal("SCIOND unable to retrieve paths: %s\n", reply.ErrorCode)
	}

	fmt.Println("Available paths to", dstIA)
	var pathStatuses map[string]string
	if *status {
		pathStatuses = getStatuses(reply.Entries)
	}
	for i, path := range reply.Entries {
		fmt.Printf("[%2d] %s", i, path.Path.String())
		if *expiration {
			fmt.Printf(" Expires: %s (%s)", path.Path.Expiry(),
				time.Until(path.Path.Expiry()).Truncate(time.Second))
		}
		if *status {
			fmt.Printf(" Status: %s", pathStatuses[string(path.Path.FwdPath)])
		}
		fmt.Printf("\n")
	}
}

func validateFlags() {
	var err error

	flag.Parse()
	log.SetupFromFlags("")

	dstIA, err = addr.IAFromString(*dstIAStr)
	if err != nil {
		LogFatal("Unable to parse destination IA: %v\n", err)
	}

	if *srcIAStr != "" {
		if srcIA, err = addr.IAFromString(*srcIAStr); err != nil {
			LogFatal("Unable to parse source IA: %v\n", err)
		}
	}

	if *sciondFromIA {
		if *sciondPath != "" {
			LogFatal("Only one of -sciond or -sciondFromIA can be specified")
		}
		if srcIA.IsZero() {
			LogFatal("-srcIA flag is missing")
		}
		*sciondPath = sciond.GetDefaultSCIONDPath(&srcIA)
	} else if *sciondPath == "" {
		*sciondPath = sciond.GetDefaultSCIONDPath(nil)
	}
}

func LogFatal(msg string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, msg, a...)
	os.Exit(1)
}

func getStatuses(paths []sciond.PathReplyEntry) map[string]string {
	// Check whether paths are alive. This is done by sending a packet
	// with invalid address via the path. The border router at the destination
	// is going to reply with SCMP error. Receiving the error means that
	// the path is alive.
	if err := snet.Init(srcIA, "", reliable.DefaultDispPath); err != nil {
		LogFatal("Initializing SNET: %v\n", err)
	}
	snetConn, err := snet.ListenSCION("udp4", &Local)
	if err != nil {
		LogFatal("Listening failed: %v\n", err)
	}
	scionConn := snetConn.(*snet.SCIONConn)
	err = scionConn.SetReadDeadline(time.Now().Add(*timeout))
	if err != nil {
		LogFatal("Cannot set deadline: %v\n", err)
	}
	pathStatuses := make(map[string]string)
	for _, path := range paths {
		sendTestPacket(scionConn, path)
		pathStatuses[string(path.Path.FwdPath)] = "Timeout"
	}
	for i := len(pathStatuses); i > 0; i-- {
		path, status := receiveTestReply(scionConn)
		if path == nil {
			break
		}
		if pathStatuses[*path] != "Timeout" {
			// Two replies received for the same path.
			pathStatuses[*path] = "Unknown"
			continue
		}
		pathStatuses[*path] = status
	}
	return pathStatuses
}

func sendTestPacket(scionConn *snet.SCIONConn, path sciond.PathReplyEntry) {
	sPath := spath.New(path.Path.FwdPath)
	if err := sPath.InitOffsets(); err != nil {
		LogFatal("Unable to initialize path: %v\n", err)
	}
	nextHop, err := path.HostInfo.Overlay()
	if err != nil {
		LogFatal("Cannot get overlay info: %v\n", err)
	}
	addr := &snet.Addr{
		IA: dstIA,
		Host: &addr.AppAddr{
			L3: addr.HostSVCFromString("NONE"),
			L4: addr.NewL4UDPInfo(0),
		},
		NextHop: nextHop,
		Path:    sPath,
	}
	log.Debug("Sending test packet.", "path", path.Path.String())
	_, err = scionConn.WriteTo([]byte{}, addr)
	if err != nil {
		LogFatal("Cannot send packet: %v\n", err)
	}
}

func receiveTestReply(scionConn *snet.SCIONConn) (*string, string) {
	b := make([]byte, 1500, 1500)
	_, addr, err := scionConn.ReadFromSCION(b)
	if addr == nil {
		if basicErr, ok := err.(common.BasicError); ok {
			if netErr, ok := basicErr.Err.(net.Error); ok && netErr.Timeout() {
				// Timeout expired before all replies were received.
				return nil, ""
			}
		}
		if err != nil {
			LogFatal("Cannot read packet: %v\n", err)
		}
		LogFatal("Packet without an address received: %v\n", err)
	}
	path := string(addr.Path.Raw)
	if err == nil {
		// We've got an actual reply instead of SCMP error. This should not happen.
		return &path, "Unknown"
	}
	if opErr, ok := err.(*snet.OpError); ok {
		if opErr.SCMP().Class == scmp.C_Routing && opErr.SCMP().Type == scmp.T_R_BadHost {
			// Expected outcome. The peer complains about SvcNone being an invalid address.
			return &path, "Alive"
		}
	}
	// All other errors are just reported alongside the path.
	return &path, err.Error()
}
<commit_msg>Add copyright notice for commit dff8442ff37d507d1c5b4ff1e2fcc2dbebf63cb1 (#2117)<commit_after>// Copyright 2018 ETH Zurich, Anapaya Systems
//
// 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.

// Simple show paths application for SCION.
package main

import (
	"flag"
	"fmt"
	"net"
	"os"
	"time"

	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/common"
	"github.com/scionproto/scion/go/lib/log"
	"github.com/scionproto/scion/go/lib/sciond"
	"github.com/scionproto/scion/go/lib/scmp"
	"github.com/scionproto/scion/go/lib/snet"
	"github.com/scionproto/scion/go/lib/sock/reliable"
	"github.com/scionproto/scion/go/lib/spath"
)

var (
	dstIAStr     = flag.String("dstIA", "", "Destination IA address: ISD-AS")
	srcIAStr     = flag.String("srcIA", "", "Source IA address: ISD-AS")
	sciondPath   = flag.String("sciond", "", "SCIOND socket path")
	timeout      = flag.Duration("timeout", 5*time.Second, "Timeout in seconds")
	maxPaths     = flag.Int("maxpaths", 10, "Maximum number of paths")
	sciondFromIA = flag.Bool("sciondFromIA", false, "SCIOND socket path from IA address:ISD-AS")
	expiration   = flag.Bool("expiration", false, "Show path expiration timestamps")
	refresh      = flag.Bool("refresh", false, "Set refresh flag for SCIOND path request")
	status       = flag.Bool("p", false, "Probe the paths and print out the statuses")
	Local        snet.Addr
)

var (
	dstIA addr.IA
	srcIA addr.IA
)

func main() {
	var err error

	flag.Var((*snet.Addr)(&Local), "local", "Local address to use for health checks")

	log.AddLogConsFlags()
	validateFlags()

	sd := sciond.NewService(*sciondPath)
	sdConn, err := sd.ConnectTimeout(*timeout)
	if err != nil {
		LogFatal("Failed to connect to SCIOND: %v\n", err)
	}
	reply, err := sdConn.Paths(dstIA, srcIA, uint16(*maxPaths),
		sciond.PathReqFlags{Refresh: *refresh})
	if err != nil {
		LogFatal("Failed to retrieve paths from SCIOND: %v\n", err)
	}
	if reply.ErrorCode != sciond.ErrorOk {
		LogFatal("SCIOND unable to retrieve paths: %s\n", reply.ErrorCode)
	}

	fmt.Println("Available paths to", dstIA)
	var pathStatuses map[string]string
	if *status {
		pathStatuses = getStatuses(reply.Entries)
	}
	for i, path := range reply.Entries {
		fmt.Printf("[%2d] %s", i, path.Path.String())
		if *expiration {
			fmt.Printf(" Expires: %s (%s)", path.Path.Expiry(),
				time.Until(path.Path.Expiry()).Truncate(time.Second))
		}
		if *status {
			fmt.Printf(" Status: %s", pathStatuses[string(path.Path.FwdPath)])
		}
		fmt.Printf("\n")
	}
}

func validateFlags() {
	var err error

	flag.Parse()
	log.SetupFromFlags("")

	dstIA, err = addr.IAFromString(*dstIAStr)
	if err != nil {
		LogFatal("Unable to parse destination IA: %v\n", err)
	}

	if *srcIAStr != "" {
		if srcIA, err = addr.IAFromString(*srcIAStr); err != nil {
			LogFatal("Unable to parse source IA: %v\n", err)
		}
	}

	if *sciondFromIA {
		if *sciondPath != "" {
			LogFatal("Only one of -sciond or -sciondFromIA can be specified")
		}
		if srcIA.IsZero() {
			LogFatal("-srcIA flag is missing")
		}
		*sciondPath = sciond.GetDefaultSCIONDPath(&srcIA)
	} else if *sciondPath == "" {
		*sciondPath = sciond.GetDefaultSCIONDPath(nil)
	}
}

func LogFatal(msg string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, msg, a...)
	os.Exit(1)
}

func getStatuses(paths []sciond.PathReplyEntry) map[string]string {
	// Check whether paths are alive. This is done by sending a packet
	// with invalid address via the path. The border router at the destination
	// is going to reply with SCMP error. Receiving the error means that
	// the path is alive.
	if err := snet.Init(srcIA, "", reliable.DefaultDispPath); err != nil {
		LogFatal("Initializing SNET: %v\n", err)
	}
	snetConn, err := snet.ListenSCION("udp4", &Local)
	if err != nil {
		LogFatal("Listening failed: %v\n", err)
	}
	scionConn := snetConn.(*snet.SCIONConn)
	err = scionConn.SetReadDeadline(time.Now().Add(*timeout))
	if err != nil {
		LogFatal("Cannot set deadline: %v\n", err)
	}
	pathStatuses := make(map[string]string)
	for _, path := range paths {
		sendTestPacket(scionConn, path)
		pathStatuses[string(path.Path.FwdPath)] = "Timeout"
	}
	for i := len(pathStatuses); i > 0; i-- {
		path, status := receiveTestReply(scionConn)
		if path == nil {
			break
		}
		if pathStatuses[*path] != "Timeout" {
			// Two replies received for the same path.
			pathStatuses[*path] = "Unknown"
			continue
		}
		pathStatuses[*path] = status
	}
	return pathStatuses
}

func sendTestPacket(scionConn *snet.SCIONConn, path sciond.PathReplyEntry) {
	sPath := spath.New(path.Path.FwdPath)
	if err := sPath.InitOffsets(); err != nil {
		LogFatal("Unable to initialize path: %v\n", err)
	}
	nextHop, err := path.HostInfo.Overlay()
	if err != nil {
		LogFatal("Cannot get overlay info: %v\n", err)
	}
	addr := &snet.Addr{
		IA: dstIA,
		Host: &addr.AppAddr{
			L3: addr.HostSVCFromString("NONE"),
			L4: addr.NewL4UDPInfo(0),
		},
		NextHop: nextHop,
		Path:    sPath,
	}
	log.Debug("Sending test packet.", "path", path.Path.String())
	_, err = scionConn.WriteTo([]byte{}, addr)
	if err != nil {
		LogFatal("Cannot send packet: %v\n", err)
	}
}

func receiveTestReply(scionConn *snet.SCIONConn) (*string, string) {
	b := make([]byte, 1500, 1500)
	_, addr, err := scionConn.ReadFromSCION(b)
	if addr == nil {
		if basicErr, ok := err.(common.BasicError); ok {
			if netErr, ok := basicErr.Err.(net.Error); ok && netErr.Timeout() {
				// Timeout expired before all replies were received.
				return nil, ""
			}
		}
		if err != nil {
			LogFatal("Cannot read packet: %v\n", err)
		}
		LogFatal("Packet without an address received: %v\n", err)
	}
	path := string(addr.Path.Raw)
	if err == nil {
		// We've got an actual reply instead of SCMP error. This should not happen.
		return &path, "Unknown"
	}
	if opErr, ok := err.(*snet.OpError); ok {
		if opErr.SCMP().Class == scmp.C_Routing && opErr.SCMP().Type == scmp.T_R_BadHost {
			// Expected outcome. The peer complains about SvcNone being an invalid address.
			return &path, "Alive"
		}
	}
	// All other errors are just reported alongside the path.
	return &path, err.Error()
}
<|endoftext|>


<commit_before>package tournament

import (
	"fmt"
	"io"
	"strings"
)

type stat struct {
	MatchesPlayed int
	Wins          int
	Draws         int
	Loses         int
	Points        int
}

// Define a function Tally(io.Reader, io.Writer) error.
func Tally(r io.Reader, w io.Writer) error {
	lines, err := getLines(r)
	if err != nil {
		return err
	}
	teamsToScores := map[string]*stat{}
	for _, line := range lines {
		if isComment(line) {
			continue
		}
		// fmt.Printf("line: %s", line)
		parts := strings.Split(line, ";")
		if len(parts) < 3 {
			continue
		}
		fmt.Printf("parts: %s", parts)
		a := parts[0]
		b := parts[1]
		// _ := parts[2]

		if _, ok := teamsToScores[a]; !ok {
			teamsToScores[a] = &stat{}
		}
		if _, ok := teamsToScores[b]; !ok {
			teamsToScores[b] = &stat{}
		}

		teamsToScores[a].MatchesPlayed += 1
		teamsToScores[b].MatchesPlayed += 1
	}

	io.WriteString(w, getTable(teamsToScores))
	return nil
}

func isComment(line string) bool {
	return strings.HasPrefix(line, "#")
}

func getLines(r io.Reader) ([]string, error) {
	input, err := io.ReadAll(r)
	if err != nil {
		return []string{}, err
	}
	lines := strings.Split(string(input), "\n")
	return lines, nil
}

func getTable(teamsToScores map[string]*stat) (result string) {
	// const header = "Team                           | MP |  W |  D |  L |  P"
	var header = fmt.Sprintf("%-30s |%3s |%3s |%3s |%3s |%3s\n", "Team", "MP", "W", "D", "L", "P")
	result += header
	for team, stat := range teamsToScores {
		result += fmt.Sprintf("%-30s |%3d |%3d |%3d |%3d |%3d\n", team, stat.MatchesPlayed, stat.Wins, stat.Draws, stat.Loses, stat.Points)
	}
	return result
}
<commit_msg>Sort teams by points then alphabetically<commit_after>package tournament

import (
	"fmt"
	"io"
	"sort"
	"strings"
)

type stat struct {
	Name          string
	MatchesPlayed int
	Wins          int
	Draws         int
	Loses         int
	Points        int
}

// Define a function Tally(io.Reader, io.Writer) error.
func Tally(r io.Reader, w io.Writer) error {
	lines, err := getLines(r)
	if err != nil {
		return err
	}
	teamsToScores := map[string]*stat{}
	for _, line := range lines {
		if isComment(line) {
			continue
		}
		// fmt.Printf("line: %s", line)
		parts := strings.Split(line, ";")
		if len(parts) < 3 {
			continue
		}
		// fmt.Printf("parts: %s", parts)
		a := parts[0]
		b := parts[1]
		outcome := parts[2]

		if _, ok := teamsToScores[a]; !ok {
			teamsToScores[a] = &stat{
				Name: a,
			}
		}
		if _, ok := teamsToScores[b]; !ok {
			teamsToScores[b] = &stat{
				Name: b,
			}
		}

		incrementMatchesPlayed(teamsToScores, a)
		incrementMatchesPlayed(teamsToScores, b)

		switch outcome {
		case "win":
			teamsToScores[a].Wins += 1
			teamsToScores[a].Points += 3
			teamsToScores[b].Loses += 1
		case "loss":
			teamsToScores[a].Loses += 1
			teamsToScores[b].Wins += 1
			teamsToScores[b].Points += 3
		case "draw":
			teamsToScores[a].Draws += 1
			teamsToScores[b].Draws += 1
			teamsToScores[a].Points += 1
			teamsToScores[b].Points += 1
		default:
			return fmt.Errorf("unexpected outcome %s", outcome)
		}
	}

	io.WriteString(w, getTable(teamsToScores))
	return nil
}

func getTable(teamsToScores map[string]*stat) (result string) {
	sorted := sortTeamsByPointsThenAlphabetically(teamsToScores)
	var header = fmt.Sprintf("%-30s |%3s |%3s |%3s |%3s |%3s\n", "Team", "MP", "W", "D", "L", "P")
	result += header
	for _, stat := range sorted {
		result += fmt.Sprintf("%-30s |%3d |%3d |%3d |%3d |%3d\n", stat.Name, stat.MatchesPlayed, stat.Wins, stat.Draws, stat.Loses, stat.Points)
	}
	return result
}

func sortTeamsByPointsThenAlphabetically(teamsToScores map[string]*stat) (result []*stat) {
	for _, stat := range teamsToScores {
		result = append(result, stat)
	}
	sort.SliceStable(result, func(i, j int) bool {
		if result[i].Points != result[j].Points {
			return result[i].Points > result[j].Points
		}
		return result[i].Name < result[j].Name
	})

	return result
}

func isComment(line string) bool {
	return strings.HasPrefix(line, "#")
}

func getLines(r io.Reader) ([]string, error) {
	input, err := io.ReadAll(r)
	if err != nil {
		return []string{}, err
	}
	lines := strings.Split(string(input), "\n")
	return lines, nil
}

func incrementMatchesPlayed(teamsToScores map[string]*stat, team string) {
	teamsToScores[team].MatchesPlayed += 1
}
<|endoftext|>