<commit_before>package debos

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path"
)

type ChrootEnterMethod int

const (
	CHROOT_METHOD_NONE   = iota // use nspawn to create the chroot environment
	CHROOT_METHOD_NSPAWN        // No chroot in use
	CHROOT_METHOD_CHROOT        // use chroot to create the chroot environment
)

type Command struct {
	Architecture string            // Architecture of the chroot, nil if same as host
	Dir          string            // Working dir to run command in
	Chroot       string            // Run in the chroot at path
	ChrootMethod ChrootEnterMethod // Method to enter the chroot

	bindMounts []string /// Items to bind mount
	extraEnv   []string // Extra environment variables to set
}

type commandWrapper struct {
	label  string
	buffer *bytes.Buffer
}

func newCommandWrapper(label string) *commandWrapper {
	b := bytes.Buffer{}
	return &commandWrapper{label, &b}
}

func (w commandWrapper) out(atEOF bool) {
	for {
		s, err := w.buffer.ReadString('\n')
		if err == nil {
			log.Printf("%s | %v", w.label, s)
		} else {
			if len(s) > 0 {
				if atEOF && err == io.EOF {
					log.Printf("%s | %v\n", w.label, s)
				} else {
					w.buffer.WriteString(s)
				}
			}
			break
		}
	}
}

func (w commandWrapper) Write(p []byte) (n int, err error) {
	n, err = w.buffer.Write(p)
	w.out(false)
	return
}

func (w *commandWrapper) flush() {
	w.out(true)
}

func NewChrootCommandForContext(context DebosContext) Command {
	c := Command{Architecture: context.Architecture, Chroot: context.Rootdir, ChrootMethod: CHROOT_METHOD_NSPAWN}

	if context.Image != "" {
		path, err := RealPath(context.Image)
		if err == nil {
			c.AddBindMount(path, "")
		} else {
			log.Printf("Failed to get realpath for %s, %v", context.Image, err)
		}
		for _, p := range context.ImagePartitions {
			path, err := RealPath(p.DevicePath)
			if err != nil {
				log.Printf("Failed to get realpath for %s, %v", p.DevicePath, err)
				continue
			}
			c.AddBindMount(path, "")
		}
		c.AddBindMount("/dev/disk", "")
	}

	return c
}

func (cmd *Command) AddEnv(env string) {
	cmd.extraEnv = append(cmd.extraEnv, env)
}

func (cmd *Command) AddEnvKey(key, value string) {
	cmd.extraEnv = append(cmd.extraEnv, fmt.Sprintf("%s=%s", key, value))
}

func (cmd *Command) AddBindMount(source, target string) {
	var mount string
	if target != "" {
		mount = fmt.Sprintf("%s:%s", source, target)
	} else {
		mount = source
	}

	cmd.bindMounts = append(cmd.bindMounts, mount)
}

func (cmd Command) Run(label string, cmdline ...string) error {
	q := newQemuHelper(cmd)
	q.Setup()

	var options []string
	switch cmd.ChrootMethod {
	case CHROOT_METHOD_NONE:
		options = cmdline
	case CHROOT_METHOD_CHROOT:
		options = append(options, "chroot")
		options = append(options, cmd.Chroot)
		options = append(options, cmdline...)
	case CHROOT_METHOD_NSPAWN:
		options = append(options, "systemd-nspawn", "-q", "-D", cmd.Chroot)
		for _, e := range cmd.extraEnv {
			options = append(options, "--setenv", e)

		}
		for _, b := range cmd.bindMounts {
			options = append(options, "--bind", b)

		}
		options = append(options, cmdline...)
	}

	exe := exec.Command(options[0], options[1:]...)
	w := newCommandWrapper(label)

	exe.Stdin = nil
	exe.Stdout = w
	exe.Stderr = w

	if len(cmd.extraEnv) > 0 && cmd.ChrootMethod != CHROOT_METHOD_NSPAWN {
		exe.Env = append(os.Environ(), cmd.extraEnv...)
	}

	// Disable services start/stop for commands running in chroot
	if cmd.ChrootMethod != CHROOT_METHOD_NONE {
		services := ServiceHelper{cmd.Chroot}
		services.Deny()
		defer services.Allow()
	}

	err := exe.Run()
	w.flush()
	q.Cleanup()

	return err
}

type qemuHelper struct {
	qemusrc    string
	qemutarget string
}

func newQemuHelper(c Command) qemuHelper {
	q := qemuHelper{}

	if c.Chroot == "" || c.Architecture == "" {
		return q
	}

	switch c.Architecture {
	case "armhf", "armel", "arm":
		q.qemusrc = "/usr/bin/qemu-arm-static"
	case "arm64":
		q.qemusrc = "/usr/bin/qemu-aarch64-static"
	case "amd64", "i386":
		/* Dummy, no qemu */
	default:
		log.Panicf("Don't know qemu for Architecture %s", c.Architecture)
	}

	if q.qemusrc != "" {
		q.qemutarget = path.Join(c.Chroot, q.qemusrc)
	}

	return q
}

func (q qemuHelper) Setup() error {
	if q.qemusrc == "" {
		return nil
	}
	return CopyFile(q.qemusrc, q.qemutarget, 0755)
}

func (q qemuHelper) Cleanup() {
	if q.qemusrc != "" {
		os.Remove(q.qemutarget)
	}
}
<commit_msg>Recognize the riscv64 architecture<commit_after>package debos

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path"
)

type ChrootEnterMethod int

const (
	CHROOT_METHOD_NONE   = iota // use nspawn to create the chroot environment
	CHROOT_METHOD_NSPAWN        // No chroot in use
	CHROOT_METHOD_CHROOT        // use chroot to create the chroot environment
)

type Command struct {
	Architecture string            // Architecture of the chroot, nil if same as host
	Dir          string            // Working dir to run command in
	Chroot       string            // Run in the chroot at path
	ChrootMethod ChrootEnterMethod // Method to enter the chroot

	bindMounts []string /// Items to bind mount
	extraEnv   []string // Extra environment variables to set
}

type commandWrapper struct {
	label  string
	buffer *bytes.Buffer
}

func newCommandWrapper(label string) *commandWrapper {
	b := bytes.Buffer{}
	return &commandWrapper{label, &b}
}

func (w commandWrapper) out(atEOF bool) {
	for {
		s, err := w.buffer.ReadString('\n')
		if err == nil {
			log.Printf("%s | %v", w.label, s)
		} else {
			if len(s) > 0 {
				if atEOF && err == io.EOF {
					log.Printf("%s | %v\n", w.label, s)
				} else {
					w.buffer.WriteString(s)
				}
			}
			break
		}
	}
}

func (w commandWrapper) Write(p []byte) (n int, err error) {
	n, err = w.buffer.Write(p)
	w.out(false)
	return
}

func (w *commandWrapper) flush() {
	w.out(true)
}

func NewChrootCommandForContext(context DebosContext) Command {
	c := Command{Architecture: context.Architecture, Chroot: context.Rootdir, ChrootMethod: CHROOT_METHOD_NSPAWN}

	if context.Image != "" {
		path, err := RealPath(context.Image)
		if err == nil {
			c.AddBindMount(path, "")
		} else {
			log.Printf("Failed to get realpath for %s, %v", context.Image, err)
		}
		for _, p := range context.ImagePartitions {
			path, err := RealPath(p.DevicePath)
			if err != nil {
				log.Printf("Failed to get realpath for %s, %v", p.DevicePath, err)
				continue
			}
			c.AddBindMount(path, "")
		}
		c.AddBindMount("/dev/disk", "")
	}

	return c
}

func (cmd *Command) AddEnv(env string) {
	cmd.extraEnv = append(cmd.extraEnv, env)
}

func (cmd *Command) AddEnvKey(key, value string) {
	cmd.extraEnv = append(cmd.extraEnv, fmt.Sprintf("%s=%s", key, value))
}

func (cmd *Command) AddBindMount(source, target string) {
	var mount string
	if target != "" {
		mount = fmt.Sprintf("%s:%s", source, target)
	} else {
		mount = source
	}

	cmd.bindMounts = append(cmd.bindMounts, mount)
}

func (cmd Command) Run(label string, cmdline ...string) error {
	q := newQemuHelper(cmd)
	q.Setup()

	var options []string
	switch cmd.ChrootMethod {
	case CHROOT_METHOD_NONE:
		options = cmdline
	case CHROOT_METHOD_CHROOT:
		options = append(options, "chroot")
		options = append(options, cmd.Chroot)
		options = append(options, cmdline...)
	case CHROOT_METHOD_NSPAWN:
		options = append(options, "systemd-nspawn", "-q", "-D", cmd.Chroot)
		for _, e := range cmd.extraEnv {
			options = append(options, "--setenv", e)

		}
		for _, b := range cmd.bindMounts {
			options = append(options, "--bind", b)

		}
		options = append(options, cmdline...)
	}

	exe := exec.Command(options[0], options[1:]...)
	w := newCommandWrapper(label)

	exe.Stdin = nil
	exe.Stdout = w
	exe.Stderr = w

	if len(cmd.extraEnv) > 0 && cmd.ChrootMethod != CHROOT_METHOD_NSPAWN {
		exe.Env = append(os.Environ(), cmd.extraEnv...)
	}

	// Disable services start/stop for commands running in chroot
	if cmd.ChrootMethod != CHROOT_METHOD_NONE {
		services := ServiceHelper{cmd.Chroot}
		services.Deny()
		defer services.Allow()
	}

	err := exe.Run()
	w.flush()
	q.Cleanup()

	return err
}

type qemuHelper struct {
	qemusrc    string
	qemutarget string
}

func newQemuHelper(c Command) qemuHelper {
	q := qemuHelper{}

	if c.Chroot == "" || c.Architecture == "" {
		return q
	}

	switch c.Architecture {
	case "armhf", "armel", "arm":
		q.qemusrc = "/usr/bin/qemu-arm-static"
	case "arm64":
		q.qemusrc = "/usr/bin/qemu-aarch64-static"
	case "riscv64":
		q.qemusrc = "/usr/bin/qemu-riscv64-static"
	case "amd64", "i386":
		/* Dummy, no qemu */
	default:
		log.Panicf("Don't know qemu for Architecture %s", c.Architecture)
	}

	if q.qemusrc != "" {
		q.qemutarget = path.Join(c.Chroot, q.qemusrc)
	}

	return q
}

func (q qemuHelper) Setup() error {
	if q.qemusrc == "" {
		return nil
	}
	return CopyFile(q.qemusrc, q.qemutarget, 0755)
}

func (q qemuHelper) Cleanup() {
	if q.qemusrc != "" {
		os.Remove(q.qemutarget)
	}
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"

	"github.com/jwaldrip/odin/cli/values"
)

// Flag returns the Value interface to the value of the named flag,
// returning nil if none exists.
func (cmd *CLI) Flag(name string) values.Value {
	flag := cmd.getFlag(name)
	value := cmd.flagValues[flag]
	return value
}

// Flags returns the flags as a map of strings with Values
func (cmd *CLI) Flags() values.Map {
	flags := make(values.Map)
	for name := range cmd.flags {
		flags[name] = cmd.Flag(name)
	}
	return flags
}

func (cmd *CLI) getFlag(name string) *Flag {
	var ok bool
	var flag *Flag
	flag, ok = cmd.flags[name]
	if !ok {
		flag, ok = cmd.inheritedFlags[name]
	}
	if !ok {
		panic(fmt.Sprintf("flag not defined %v", name))
	}
	return flag
}
<commit_msg>simplify check<commit_after>package cli

import (
	"fmt"

	"github.com/jwaldrip/odin/cli/values"
)

// Flag returns the Value interface to the value of the named flag,
// returning nil if none exists.
func (cmd *CLI) Flag(name string) values.Value {
	flag := cmd.getFlag(name)
	value := cmd.flagValues[flag]
	return value
}

// Flags returns the flags as a map of strings with Values
func (cmd *CLI) Flags() values.Map {
	flags := make(values.Map)
	for name := range cmd.flags {
		flags[name] = cmd.Flag(name)
	}
	return flags
}

func (cmd *CLI) getFlag(name string) *Flag {
	var ok bool
	var flag *Flag
	flag, ok = cmd.inheritedFlags.Merge(cmd.flags)[name]
	if !ok {
		panic(fmt.Sprintf("flag not defined %v", name))
	}
	return flag
}
<|endoftext|>


<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"
)

//Const parts -----------------------------------------------------------
const SPACESIZE = 160

//Gloabl var part -------------------------------------------------------

//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 (d *DHTnode) SendPrintRing(destination *shared.DistantNode, currentString *string) {

}

func (currentNode *DHTnode) AddToRing(newNode *shared.DistantNode) {

	whereToInsert := currentNode.Lookup(newNode.Id)
	currentNode.commLib.SendUpdateSuccessor(whereToInsert, newNode)
}

//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) {
	//possible TODO : condition on the origin of the message for this sending ?
	d.commLib.SendUpdatePredecessor(d.Successor, newNode)
	d.Successor = newNode
	d.commLib.SendUpdatePredecessor(newNode, d.ToDistantNode())
}

func (d *DHTnode) updatePredecessor(newNode *shared.DistantNode) {
	d.Predecessor = newNode
	d.commLib.SendUpdateSuccessor(newNode, d.ToDistantNode())
	d.initFingersTable()
}

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.Successor.Id:
		{
			return true
		}
	case dht.Between(shared.LocalId, currentNode.Successor.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]\n", 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
		return currentNode.commLib.SendLookup(currentNode.findClosestNode(IdToSearch), IdToSearch)
	}

}

func (currentNode *DHTnode) findClosestNode(IdToSearch string) *shared.DistantNode {
	bestFinger := currentNode.Successor

	minDistance := dht.Distance([]byte(currentNode.Successor.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 {
						// fmt.Printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Better finger ellected ! number [%d] ->[%s]\n", i, v.nodeResp.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) initFingersTable() {
	// 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)
		node.fingers[i] = &fingerEntry{fingerId, &shared.DistantNode{responsibleNode.Id, responsibleNode.Ip, responsibleNode.Port}}

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

func (node *DHTnode) printRing(currentString *string) {
	if currentString == nil {
		currentString = new(string)
	}
	node.PrintNodeName(currentString)
	node.commLib.SendPrintRing(node.Successor, currentString)
}

func (node *DHTnode) PrintRing() {
	node.printRing(nil)
}

func (node *DHTnode) PrintNodeName(currentString *string) {
	fmt.Printf("%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)

// 	}
// }

//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] created with its sender Interface", shared.LocalId)
	return &daNode, daComInterface
}
<commit_msg>making : -node.IsResponsible - node .FindClosestNode<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"
)

//Const parts -----------------------------------------------------------
const SPACESIZE = 160

//Gloabl var part -------------------------------------------------------

//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 (d *DHTnode) SendPrintRing(destination *shared.DistantNode, currentString *string) {

}

func (currentNode *DHTnode) AddToRing(newNode *shared.DistantNode) {

	whereToInsert := currentNode.Lookup(newNode.Id)
	currentNode.commLib.SendUpdateSuccessor(whereToInsert, newNode)
}

//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) {
	//possible TODO : condition on the origin of the message for this sending ?
	d.commLib.SendUpdatePredecessor(d.Successor, newNode)
	d.Successor = newNode
	d.commLib.SendUpdatePredecessor(newNode, d.ToDistantNode())
}

func (d *DHTnode) updatePredecessor(newNode *shared.DistantNode) {
	d.Predecessor = newNode
	d.commLib.SendUpdateSuccessor(newNode, d.ToDistantNode())
	d.initFingersTable()
}

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.Successor.Id:
		{
			return true
		}
	case dht.Between(shared.LocalId, currentNode.Successor.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]\n", 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
		return currentNode.commLib.SendLookup(currentNode.findClosestNode(IdToSearch), IdToSearch)
	}

}

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

	minDistance := dht.Distance([]byte(currentNode.Successor.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 {
						// fmt.Printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Better finger ellected ! number [%d] ->[%s]\n", i, v.nodeResp.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) initFingersTable() {
	// 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)
		node.fingers[i] = &fingerEntry{fingerId, &shared.DistantNode{responsibleNode.Id, responsibleNode.Ip, responsibleNode.Port}}

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

func (node *DHTnode) printRing(currentString *string) {
	if currentString == nil {
		currentString = new(string)
	}
	node.PrintNodeName(currentString)
	node.commLib.SendPrintRing(node.Successor, currentString)
}

func (node *DHTnode) PrintRing() {
	node.printRing(nil)
}

func (node *DHTnode) PrintNodeName(currentString *string) {
	fmt.Printf("%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)

// 	}
// }

//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] created with its sender Interface", shared.LocalId)
	return &daNode, daComInterface
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Google 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 heartbeat

import (
	"errors"
	"time"

	"github.com/cloudfoundry/lager"
)

type Heartbeater interface {
	Start()
	Increment(string)
	Stop()
}

type Handler interface {
	Handle(string)
	Flush() error
}

type heartbeater struct {
	logger   lager.Logger
	trigger  <-chan time.Time
	counter  chan string
	done     chan struct{}
	started  bool
	handlers []Handler
}

func NewHeartbeater(logger lager.Logger, trigger <-chan time.Time) Heartbeater {
	counter := make(chan string)
	done := make(chan struct{})
	loggerHandler := NewLoggerHandler(logger)
	return &heartbeater{
		trigger: trigger,
		counter: counter,
		done:    done,
		started: false,
		logger:  logger,
		handlers: []Handler{
			loggerHandler,
		},
	}
}

func NewLoggerMetricHeartbeater(metricHandler Handler, logger lager.Logger, trigger <-chan time.Time) Heartbeater {
	counter := make(chan string)
	done := make(chan struct{})
	loggerHandler := NewLoggerHandler(logger)
	return &heartbeater{
		trigger: trigger,
		counter: counter,
		done:    done,
		started: false,
		logger:  logger,
		handlers: []Handler{
			loggerHandler,
			metricHandler,
		},
	}
}
func (h *heartbeater) Start() {
	h.started = true
	go func() {
		for {
			select {
			case <-h.trigger:
				for _, h := range h.handlers {
					h.Flush()
				}
			case name := <-h.counter:
				for _, h := range h.handlers {
					h.Handle(name)
				}
			case <-h.done:
				for _, h := range h.handlers {
					h.Flush()
				}
				return
			}
		}
	}()
}

func (h *heartbeater) Increment(name string) {
	if h.started {
		h.counter <- name
	} else {
		h.logger.Error(
			"heartbeater",
			errors.New("attempted to increment counter without starting heartbeater"),
		)
	}
}

func (h *heartbeater) Stop() {
	h.done <- struct{}{}
	h.started = false
}

func (h *heartbeater) AddHandler(handler Handler) {
	h.handlers = append(h.handlers, handler)
}
<commit_msg>hearbeat: remove AddHandler method<commit_after>/*
 * Copyright 2017 Google 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 heartbeat

import (
	"errors"
	"time"

	"github.com/cloudfoundry/lager"
)

type Heartbeater interface {
	Start()
	Increment(string)
	Stop()
}

type Handler interface {
	Handle(string)
	Flush() error
}

type heartbeater struct {
	logger   lager.Logger
	trigger  <-chan time.Time
	counter  chan string
	done     chan struct{}
	started  bool
	handlers []Handler
}

func NewHeartbeater(logger lager.Logger, trigger <-chan time.Time) Heartbeater {
	counter := make(chan string)
	done := make(chan struct{})
	loggerHandler := NewLoggerHandler(logger)
	return &heartbeater{
		trigger: trigger,
		counter: counter,
		done:    done,
		started: false,
		logger:  logger,
		handlers: []Handler{
			loggerHandler,
		},
	}
}

func NewLoggerMetricHeartbeater(metricHandler Handler, logger lager.Logger, trigger <-chan time.Time) Heartbeater {
	counter := make(chan string)
	done := make(chan struct{})
	loggerHandler := NewLoggerHandler(logger)
	return &heartbeater{
		trigger: trigger,
		counter: counter,
		done:    done,
		started: false,
		logger:  logger,
		handlers: []Handler{
			loggerHandler,
			metricHandler,
		},
	}
}
func (h *heartbeater) Start() {
	h.started = true
	go func() {
		for {
			select {
			case <-h.trigger:
				for _, h := range h.handlers {
					h.Flush()
				}
			case name := <-h.counter:
				for _, h := range h.handlers {
					h.Handle(name)
				}
			case <-h.done:
				for _, h := range h.handlers {
					h.Flush()
				}
				return
			}
		}
	}()
}

func (h *heartbeater) Increment(name string) {
	if h.started {
		h.counter <- name
	} else {
		h.logger.Error(
			"heartbeater",
			errors.New("attempted to increment counter without starting heartbeater"),
		)
	}
}

func (h *heartbeater) Stop() {
	h.done <- struct{}{}
	h.started = false
}
<|endoftext|>


<commit_before>package datastore

import (
	"encoding/json"
	. "launchpad.net/gocheck"
	"os"
	"parser"
	"protocol"
	"testing"
	"time"
)

// Hook up gocheck into the gotest runner.
func Test(t *testing.T) {
	TestingT(t)
}

type DatastoreSuite struct{}

var _ = Suite(&DatastoreSuite{})

const DB_DIR = "/tmp/chronosdb/datastore_test"

func newDatastore(c *C) Datastore {
	os.MkdirAll(DB_DIR, 0744)
	db, err := NewLevelDbDatastore(DB_DIR)
	c.Assert(err, Equals, nil)
	return db
}

func cleanup(db Datastore) {
	db.Close()
	os.RemoveAll(DB_DIR)
}

func stringToSeries(seriesString string, timestamp int64, c *C) *protocol.Series {
	series := &protocol.Series{}
	err := json.Unmarshal([]byte(seriesString), &series)
	c.Assert(err, IsNil)
	for _, point := range series.Points {
		point.Timestamp = &timestamp
	}
	return series
}

func (self *DatastoreSuite) TestCanWriteAndRetrievePoints(c *C) {
	db := newDatastore(c)
	defer cleanup(db)
	mock := `
  {
    "points": [
      {
        "values": [
          {
            "int_value": 2
          }
        ],
        "sequence_number": 2
      },
      {
        "values": [
          {
            "int_value": 3
          }
        ],
        "sequence_number": 1
      }
    ],
    "name": "foo",
    "fields": [
      {
        "type": "INT32",
        "name": "value"
      }
    ]
  }`
	pointTime := time.Now().Unix()
	series := stringToSeries(mock, pointTime, c)
	err := db.WriteSeriesData("test", series)
	c.Assert(err, IsNil)
	q, errQ := parser.ParseQuery("select value from foo;")
	c.Assert(errQ, IsNil)
	done := make(chan int, 1)
	resultSeries := &protocol.Series{}
	yield := func(series *protocol.Series) error {
		resultSeries = series
		done <- 1
		return nil
	}
	err = db.ExecuteQuery("test", q, yield)
	c.Assert(err, IsNil)
	<-done
	c.Assert(resultSeries, Not(IsNil))
	c.Assert(len(resultSeries.Points), Equals, 2)
	c.Assert(len(resultSeries.Fields), Equals, 1)
	c.Assert(*resultSeries.Points[0].SequenceNumber, Equals, uint32(2))
	c.Assert(*resultSeries.Points[1].SequenceNumber, Equals, uint32(1))
	c.Assert(*resultSeries.Points[0].Timestamp, Equals, pointTime)
	c.Assert(*resultSeries.Points[1].Timestamp, Equals, pointTime)
	c.Assert(*resultSeries.Points[0].Values[0].IntValue, Equals, int32(2))
	c.Assert(*resultSeries.Points[1].Values[0].IntValue, Equals, int32(3))
}
<commit_msg>Add test to make sure it persists data<commit_after>package datastore

import (
	"encoding/json"
	. "launchpad.net/gocheck"
	"os"
	"parser"
	"protocol"
	"testing"
	"time"
)

// Hook up gocheck into the gotest runner.
func Test(t *testing.T) {
	TestingT(t)
}

type DatastoreSuite struct{}

var _ = Suite(&DatastoreSuite{})

const DB_DIR = "/tmp/chronosdb/datastore_test"

func newDatastore(c *C) Datastore {
	os.MkdirAll(DB_DIR, 0744)
	db, err := NewLevelDbDatastore(DB_DIR)
	c.Assert(err, Equals, nil)
	return db
}

func cleanup(db Datastore) {
	if db != nil {
		db.Close()
	}
	os.RemoveAll(DB_DIR)
}

func stringToSeries(seriesString string, timestamp int64, c *C) *protocol.Series {
	series := &protocol.Series{}
	err := json.Unmarshal([]byte(seriesString), &series)
	c.Assert(err, IsNil)
	for _, point := range series.Points {
		point.Timestamp = &timestamp
	}
	return series
}

func executeQuery(database, query string, db Datastore, c *C) *protocol.Series {
	q, errQ := parser.ParseQuery(query)
	c.Assert(errQ, IsNil)
	done := make(chan int, 1)
	resultSeries := &protocol.Series{}
	yield := func(series *protocol.Series) error {
		resultSeries = series
		done <- 1
		return nil
	}
	err := db.ExecuteQuery(database, q, yield)
	c.Assert(err, IsNil)
	<-done
	return resultSeries
}

func (self *DatastoreSuite) TestCanWriteAndRetrievePoints(c *C) {
	cleanup(nil)
	db := newDatastore(c)
	defer cleanup(db)
	mock := `
  {
    "points": [
      {
        "values": [
          {
            "int_value": 3
          }
        ],
        "sequence_number": 1
      },
      {
        "values": [
          {
            "int_value": 2
          }
        ],
        "sequence_number": 2
      }
    ],
    "name": "foo",
    "fields": [
      {
        "type": "INT32",
        "name": "value"
      }
    ]
  }`
	pointTime := time.Now().Unix()
	series := stringToSeries(mock, pointTime, c)
	err := db.WriteSeriesData("test", series)
	c.Assert(err, IsNil)
	q, errQ := parser.ParseQuery("select value from foo;")
	c.Assert(errQ, IsNil)
	done := make(chan int, 1)
	resultSeries := &protocol.Series{}
	yield := func(series *protocol.Series) error {
		resultSeries = series
		done <- 1
		return nil
	}
	err = db.ExecuteQuery("test", q, yield)
	c.Assert(err, IsNil)
	<-done
	c.Assert(resultSeries, Not(IsNil))
	c.Assert(len(resultSeries.Points), Equals, 2)
	c.Assert(len(resultSeries.Fields), Equals, 1)
	c.Assert(*resultSeries.Points[0].SequenceNumber, Equals, uint32(2))
	c.Assert(*resultSeries.Points[1].SequenceNumber, Equals, uint32(1))
	c.Assert(*resultSeries.Points[0].Timestamp, Equals, pointTime)
	c.Assert(*resultSeries.Points[1].Timestamp, Equals, pointTime)
	c.Assert(*resultSeries.Points[0].Values[0].IntValue, Equals, int32(2))
	c.Assert(*resultSeries.Points[1].Values[0].IntValue, Equals, int32(3))
	c.Assert(resultSeries, Not(DeepEquals), series)
}

func (self *DatastoreSuite) TestCanPersistDataAndWriteNewData(c *C) {
	cleanup(nil)
	db := newDatastore(c)
	mock := `
  {
    "points": [
      {
        "values": [
          {
            "int_value": 3
          }
        ],
        "sequence_number": 1
      }
    ],
    "name": "foo",
    "fields": [
      {
        "type": "INT32",
        "name": "value"
      }
    ]
  }`
	series := stringToSeries(mock, time.Now().Unix(), c)
	err := db.WriteSeriesData("asdf", series)
	c.Assert(err, IsNil)
	results := executeQuery("asdf", "select value from foo;", db, c)
	c.Assert(results, DeepEquals, series)
	db.Close()
	db = newDatastore(c)
	defer cleanup(db)
	results = executeQuery("asdf", "select value from foo;", db, c)
	c.Assert(results, DeepEquals, series)
}
<|endoftext|>


<commit_before>package project

import (
	"errors"
	"fmt"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/rancher/go-rancher/catalog"
	"github.com/rancher/go-rancher/v3"
	"github.com/rancher/rancher-compose-executor/lookup"
	"github.com/rancher/rancher-compose-executor/lookup/server"
	"github.com/rancher/rancher-compose-executor/utils"
)

func (p *Project) LoadFromTemplateVersion(templateVersion catalog.TemplateVersion, answers map[string]string) error {
	p.TemplateVersion = &templateVersion

	defaultedAnswers := map[string]string{}
	for k, v := range answers {
		defaultedAnswers[k] = v
	}

	for _, question := range templateVersion.Questions {
		if _, found := defaultedAnswers[question.Variable]; !found {
			defaultedAnswers[question.Variable] = question.Default
		}
	}

	return p.Load(templateVersion.Files, defaultedAnswers)
}

func (p *Project) Load(templates map[string]string, answers map[string]string) error {
	var err error

	p.Templates = utils.ToMapByte(templates)
	p.Answers = answers

	if p.ResourceLookup == nil {
		p.ResourceLookup = &lookup.MemoryResourceLookup{
			Content: p.Templates,
		}
	}

	if p.Name == "" {
		return errors.New("Name is required")
	}

	if stackSchema, ok := p.Client.GetTypes()["stack"]; !ok || !utils.Contains(stackSchema.CollectionMethods, "POST") {
		return fmt.Errorf("Can not create a stack, check API key [%s] for [%s]",
			p.Client.GetOpts().AccessKey,
			p.Client.GetOpts().Url)
	}

	if p.Stack == nil {
		stack, err := loadStack(p.Name, p.Client)
		if err != nil {
			return err
		}

		p.Stack = stack
	}

	if p.ServerResourceLookup == nil {
		p.ServerResourceLookup = server.NewLookup(p.Stack.Id, p.Client)
	}

	defer p.Config.Complete()

	composeBytes, file, err := p.ResourceLookup.Lookup("compose.yml", ".")
	if err == nil {
		return p.load(file, composeBytes)
	}

	composeBytes, file, err = p.ResourceLookup.Lookup("docker-compose.yml", ".")
	if err == nil {
		if err := p.load(file, composeBytes); err != nil {
			return err
		}
	} else {
		composeBytes, file, err = p.ResourceLookup.Lookup("docker-compose.yml.tpl", ".")
		if err != nil {
			return err
		}
		if err := p.load(file, composeBytes); err != nil {
			return err
		}
	}

	composeBytes, file, err = p.ResourceLookup.Lookup("rancher-compose.yml", ".")
	if err == nil {
		err = p.load(file, composeBytes)
		if err != nil {
			return err
		}
	}

	return nil
}

func loadStack(projectName string, c *client.RancherClient) (*client.Stack, error) {
	logrus.Debugf("Looking for stack %s", projectName)
	// First try by name
	stacks, err := c.Stack.List(&client.ListOpts{
		Filters: map[string]interface{}{
			"name":         projectName,
			"removed_null": nil,
		},
	})
	if err != nil {
		return nil, err
	}

	for _, stack := range stacks.Data {
		if strings.EqualFold(projectName, stack.Name) {
			logrus.Debugf("Found stack: %s(%s)", stack.Name, stack.Id)
			return &stack, nil
		}
	}

	// Now try not by name for case sensitive databases
	stacks, err = c.Stack.List(&client.ListOpts{
		Filters: map[string]interface{}{
			"removed_null": nil,
		},
	})
	if err != nil {
		return nil, err
	}

	for _, stack := range stacks.Data {
		if strings.EqualFold(projectName, stack.Name) {
			logrus.Debugf("Found stack: %s(%s)", stack.Name, stack.Id)
			return &stack, nil
		}
	}

	logrus.Infof("Creating stack %s", projectName)
	stack, err := c.Stack.Create(&client.Stack{
		Name: projectName,
	})
	if err != nil {
		return nil, err
	}

	return stack, nil
}
<commit_msg>Load all templates<commit_after>package project

import (
	"errors"
	"fmt"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/rancher/go-rancher/catalog"
	"github.com/rancher/go-rancher/v3"
	"github.com/rancher/rancher-compose-executor/lookup"
	"github.com/rancher/rancher-compose-executor/lookup/server"
	"github.com/rancher/rancher-compose-executor/utils"
)

func (p *Project) LoadFromTemplateVersion(templateVersion catalog.TemplateVersion, answers map[string]string) error {
	p.TemplateVersion = &templateVersion

	defaultedAnswers := map[string]string{}
	for k, v := range answers {
		defaultedAnswers[k] = v
	}

	for _, question := range templateVersion.Questions {
		if _, found := defaultedAnswers[question.Variable]; !found {
			defaultedAnswers[question.Variable] = question.Default
		}
	}

	return p.Load(templateVersion.Files, defaultedAnswers)
}

func (p *Project) Load(templates map[string]string, answers map[string]string) error {
	var err error

	p.Templates = utils.ToMapByte(templates)
	p.Answers = answers

	if p.ResourceLookup == nil {
		p.ResourceLookup = &lookup.MemoryResourceLookup{
			Content: p.Templates,
		}
	}

	if p.Name == "" {
		return errors.New("Name is required")
	}

	if stackSchema, ok := p.Client.GetTypes()["stack"]; !ok || !utils.Contains(stackSchema.CollectionMethods, "POST") {
		return fmt.Errorf("Can not create a stack, check API key [%s] for [%s]",
			p.Client.GetOpts().AccessKey,
			p.Client.GetOpts().Url)
	}

	if p.Stack == nil {
		stack, err := loadStack(p.Name, p.Client)
		if err != nil {
			return err
		}

		p.Stack = stack
	}

	if p.ServerResourceLookup == nil {
		p.ServerResourceLookup = server.NewLookup(p.Stack.Id, p.Client)
	}

	defer p.Config.Complete()

	for file, contents := range p.Templates {
		if err = p.load(file, contents); err != nil {
			return err
		}
	}

	return nil
}

func loadStack(projectName string, c *client.RancherClient) (*client.Stack, error) {
	logrus.Debugf("Looking for stack %s", projectName)
	// First try by name
	stacks, err := c.Stack.List(&client.ListOpts{
		Filters: map[string]interface{}{
			"name":         projectName,
			"removed_null": nil,
		},
	})
	if err != nil {
		return nil, err
	}

	for _, stack := range stacks.Data {
		if strings.EqualFold(projectName, stack.Name) {
			logrus.Debugf("Found stack: %s(%s)", stack.Name, stack.Id)
			return &stack, nil
		}
	}

	// Now try not by name for case sensitive databases
	stacks, err = c.Stack.List(&client.ListOpts{
		Filters: map[string]interface{}{
			"removed_null": nil,
		},
	})
	if err != nil {
		return nil, err
	}

	for _, stack := range stacks.Data {
		if strings.EqualFold(projectName, stack.Name) {
			logrus.Debugf("Found stack: %s(%s)", stack.Name, stack.Id)
			return &stack, nil
		}
	}

	logrus.Infof("Creating stack %s", projectName)
	stack, err := c.Stack.Create(&client.Stack{
		Name: projectName,
	})
	if err != nil {
		return nil, err
	}

	return stack, nil
}
<|endoftext|>


<commit_before>// Copyright 2017 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package promql

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"github.com/pkg/errors"

	"github.com/prometheus/prometheus/model/labels"
	"github.com/prometheus/prometheus/promql/parser"
	"github.com/prometheus/prometheus/storage"
	"github.com/prometheus/prometheus/tsdb/chunkenc"
)

func (Matrix) Type() parser.ValueType { return parser.ValueTypeMatrix }
func (Vector) Type() parser.ValueType { return parser.ValueTypeVector }
func (Scalar) Type() parser.ValueType { return parser.ValueTypeScalar }
func (String) Type() parser.ValueType { return parser.ValueTypeString }

// String represents a string value.
type String struct {
	T int64
	V string
}

func (s String) String() string {
	return s.V
}

func (s String) MarshalJSON() ([]byte, error) {
	return json.Marshal([...]interface{}{float64(s.T) / 1000, s.V})
}

// Scalar is a data point that's explicitly not associated with a metric.
type Scalar struct {
	T int64
	V float64
}

func (s Scalar) String() string {
	v := strconv.FormatFloat(s.V, 'f', -1, 64)
	return fmt.Sprintf("scalar: %v @[%v]", v, s.T)
}

func (s Scalar) MarshalJSON() ([]byte, error) {
	v := strconv.FormatFloat(s.V, 'f', -1, 64)
	return json.Marshal([...]interface{}{float64(s.T) / 1000, v})
}

// Series is a stream of data points belonging to a metric.
type Series struct {
	Metric labels.Labels `json:"metric"`
	Points []Point       `json:"values"`
}

func (s Series) String() string {
	vals := make([]string, len(s.Points))
	for i, v := range s.Points {
		vals[i] = v.String()
	}
	return fmt.Sprintf("%s =>\n%s", s.Metric, strings.Join(vals, "\n"))
}

// Point represents a single data point for a given timestamp.
type Point struct {
	T int64
	V float64
}

func (p Point) String() string {
	v := strconv.FormatFloat(p.V, 'f', -1, 64)
	return fmt.Sprintf("%v @[%v]", v, p.T)
}

// MarshalJSON implements json.Marshaler.
func (p Point) MarshalJSON() ([]byte, error) {
	v := strconv.FormatFloat(p.V, 'f', -1, 64)
	return json.Marshal([...]interface{}{float64(p.T) / 1000, v})
}

// Sample is a single sample belonging to a metric.
type Sample struct {
	Point

	Metric labels.Labels
}

func (s Sample) String() string {
	return fmt.Sprintf("%s => %s", s.Metric, s.Point)
}

func (s Sample) MarshalJSON() ([]byte, error) {
	v := struct {
		M labels.Labels `json:"metric"`
		V Point         `json:"value"`
	}{
		M: s.Metric,
		V: s.Point,
	}
	return json.Marshal(v)
}

// Vector is basically only an alias for model.Samples, but the
// contract is that in a Vector, all Samples have the same timestamp.
type Vector []Sample

func (vec Vector) String() string {
	entries := make([]string, len(vec))
	for i, s := range vec {
		entries[i] = s.String()
	}
	return strings.Join(entries, "\n")
}

// ContainsSameLabelset checks if a vector has samples with the same labelset
// Such a behavior is semantically undefined
// https://github.com/prometheus/prometheus/issues/4562
func (vec Vector) ContainsSameLabelset() bool {
	l := make(map[uint64]struct{}, len(vec))
	for _, s := range vec {
		hash := s.Metric.Hash()
		if _, ok := l[hash]; ok {
			return true
		}
		l[hash] = struct{}{}
	}
	return false
}

// Matrix is a slice of Series that implements sort.Interface and
// has a String method.
type Matrix []Series

func (m Matrix) String() string {
	// TODO(fabxc): sort, or can we rely on order from the querier?
	strs := make([]string, len(m))

	for i, ss := range m {
		strs[i] = ss.String()
	}

	return strings.Join(strs, "\n")
}

// TotalSamples returns the total number of samples in the series within a matrix.
func (m Matrix) TotalSamples() int {
	numSamples := 0
	for _, series := range m {
		numSamples += len(series.Points)
	}
	return numSamples
}

func (m Matrix) Len() int           { return len(m) }
func (m Matrix) Less(i, j int) bool { return labels.Compare(m[i].Metric, m[j].Metric) < 0 }
func (m Matrix) Swap(i, j int)      { m[i], m[j] = m[j], m[i] }

// ContainsSameLabelset checks if a matrix has samples with the same labelset.
// Such a behavior is semantically undefined.
// https://github.com/prometheus/prometheus/issues/4562
func (m Matrix) ContainsSameLabelset() bool {
	l := make(map[uint64]struct{}, len(m))
	for _, ss := range m {
		hash := ss.Metric.Hash()
		if _, ok := l[hash]; ok {
			return true
		}
		l[hash] = struct{}{}
	}
	return false
}

// Result holds the resulting value of an execution or an error
// if any occurred.
type Result struct {
	Err      error
	Value    parser.Value
	Warnings storage.Warnings
}

// Vector returns a Vector if the result value is one. An error is returned if
// the result was an error or the result value is not a Vector.
func (r *Result) Vector() (Vector, error) {
	if r.Err != nil {
		return nil, r.Err
	}
	v, ok := r.Value.(Vector)
	if !ok {
		return nil, errors.New("query result is not a Vector")
	}
	return v, nil
}

// Matrix returns a Matrix. An error is returned if
// the result was an error or the result value is not a Matrix.
func (r *Result) Matrix() (Matrix, error) {
	if r.Err != nil {
		return nil, r.Err
	}
	v, ok := r.Value.(Matrix)
	if !ok {
		return nil, errors.New("query result is not a range Vector")
	}
	return v, nil
}

// Scalar returns a Scalar value. An error is returned if
// the result was an error or the result value is not a Scalar.
func (r *Result) Scalar() (Scalar, error) {
	if r.Err != nil {
		return Scalar{}, r.Err
	}
	v, ok := r.Value.(Scalar)
	if !ok {
		return Scalar{}, errors.New("query result is not a Scalar")
	}
	return v, nil
}

func (r *Result) String() string {
	if r.Err != nil {
		return r.Err.Error()
	}
	if r.Value == nil {
		return ""
	}
	return r.Value.String()
}

// StorageSeries simulates promql.Series as storage.Series.
type StorageSeries struct {
	series Series
}

// NewStorageSeries returns a StorageSeries from a Series.
func NewStorageSeries(series Series) *StorageSeries {
	return &StorageSeries{
		series: series,
	}
}

func (ss *StorageSeries) Labels() labels.Labels {
	return ss.series.Metric
}

// Iterator returns a new iterator of the data of the series.
func (ss *StorageSeries) Iterator() chunkenc.Iterator {
	return newStorageSeriesIterator(ss.series)
}

type storageSeriesIterator struct {
	points []Point
	curr   int
}

func newStorageSeriesIterator(series Series) *storageSeriesIterator {
	return &storageSeriesIterator{
		points: series.Points,
		curr:   -1,
	}
}

func (ssi *storageSeriesIterator) Seek(t int64) bool {
	i := ssi.curr
	if i < 0 {
		i = 0
	}
	for ; i < len(ssi.points); i++ {
		if ssi.points[i].T >= t {
			ssi.curr = i
			return true
		}
	}
	ssi.curr = len(ssi.points) - 1
	return false
}

func (ssi *storageSeriesIterator) At() (t int64, v float64) {
	p := ssi.points[ssi.curr]
	return p.T, p.V
}

func (ssi *storageSeriesIterator) Next() bool {
	ssi.curr++
	return ssi.curr < len(ssi.points)
}

func (ssi *storageSeriesIterator) Err() error {
	return nil
}
<commit_msg>Promql: Add clarifying note to Point.MarshalJSON<commit_after>// Copyright 2017 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package promql

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"github.com/pkg/errors"

	"github.com/prometheus/prometheus/model/labels"
	"github.com/prometheus/prometheus/promql/parser"
	"github.com/prometheus/prometheus/storage"
	"github.com/prometheus/prometheus/tsdb/chunkenc"
)

func (Matrix) Type() parser.ValueType { return parser.ValueTypeMatrix }
func (Vector) Type() parser.ValueType { return parser.ValueTypeVector }
func (Scalar) Type() parser.ValueType { return parser.ValueTypeScalar }
func (String) Type() parser.ValueType { return parser.ValueTypeString }

// String represents a string value.
type String struct {
	T int64
	V string
}

func (s String) String() string {
	return s.V
}

func (s String) MarshalJSON() ([]byte, error) {
	return json.Marshal([...]interface{}{float64(s.T) / 1000, s.V})
}

// Scalar is a data point that's explicitly not associated with a metric.
type Scalar struct {
	T int64
	V float64
}

func (s Scalar) String() string {
	v := strconv.FormatFloat(s.V, 'f', -1, 64)
	return fmt.Sprintf("scalar: %v @[%v]", v, s.T)
}

func (s Scalar) MarshalJSON() ([]byte, error) {
	v := strconv.FormatFloat(s.V, 'f', -1, 64)
	return json.Marshal([...]interface{}{float64(s.T) / 1000, v})
}

// Series is a stream of data points belonging to a metric.
type Series struct {
	Metric labels.Labels `json:"metric"`
	Points []Point       `json:"values"`
}

func (s Series) String() string {
	vals := make([]string, len(s.Points))
	for i, v := range s.Points {
		vals[i] = v.String()
	}
	return fmt.Sprintf("%s =>\n%s", s.Metric, strings.Join(vals, "\n"))
}

// Point represents a single data point for a given timestamp.
type Point struct {
	T int64
	V float64
}

func (p Point) String() string {
	v := strconv.FormatFloat(p.V, 'f', -1, 64)
	return fmt.Sprintf("%v @[%v]", v, p.T)
}

// MarshalJSON implements json.Marshaler.
//
// JSON marshaling is only needed for the HTTP API. Since Point is such a
// frequently marshaled type, it gets an optimized treatment directly in
// web/api/v1/api.go. Therefore, this method is unused within Prometheus. It is
// still provided here as convenience for debugging and for other users of this
// code. Also note that the different marshaling implementations might lead to
// slightly different results in terms of formatting and rounding of the
// timestamp.
func (p Point) MarshalJSON() ([]byte, error) {
	v := strconv.FormatFloat(p.V, 'f', -1, 64)
	return json.Marshal([...]interface{}{float64(p.T) / 1000, v})
}

// Sample is a single sample belonging to a metric.
type Sample struct {
	Point

	Metric labels.Labels
}

func (s Sample) String() string {
	return fmt.Sprintf("%s => %s", s.Metric, s.Point)
}

func (s Sample) MarshalJSON() ([]byte, error) {
	v := struct {
		M labels.Labels `json:"metric"`
		V Point         `json:"value"`
	}{
		M: s.Metric,
		V: s.Point,
	}
	return json.Marshal(v)
}

// Vector is basically only an alias for model.Samples, but the
// contract is that in a Vector, all Samples have the same timestamp.
type Vector []Sample

func (vec Vector) String() string {
	entries := make([]string, len(vec))
	for i, s := range vec {
		entries[i] = s.String()
	}
	return strings.Join(entries, "\n")
}

// ContainsSameLabelset checks if a vector has samples with the same labelset
// Such a behavior is semantically undefined
// https://github.com/prometheus/prometheus/issues/4562
func (vec Vector) ContainsSameLabelset() bool {
	l := make(map[uint64]struct{}, len(vec))
	for _, s := range vec {
		hash := s.Metric.Hash()
		if _, ok := l[hash]; ok {
			return true
		}
		l[hash] = struct{}{}
	}
	return false
}

// Matrix is a slice of Series that implements sort.Interface and
// has a String method.
type Matrix []Series

func (m Matrix) String() string {
	// TODO(fabxc): sort, or can we rely on order from the querier?
	strs := make([]string, len(m))

	for i, ss := range m {
		strs[i] = ss.String()
	}

	return strings.Join(strs, "\n")
}

// TotalSamples returns the total number of samples in the series within a matrix.
func (m Matrix) TotalSamples() int {
	numSamples := 0
	for _, series := range m {
		numSamples += len(series.Points)
	}
	return numSamples
}

func (m Matrix) Len() int           { return len(m) }
func (m Matrix) Less(i, j int) bool { return labels.Compare(m[i].Metric, m[j].Metric) < 0 }
func (m Matrix) Swap(i, j int)      { m[i], m[j] = m[j], m[i] }

// ContainsSameLabelset checks if a matrix has samples with the same labelset.
// Such a behavior is semantically undefined.
// https://github.com/prometheus/prometheus/issues/4562
func (m Matrix) ContainsSameLabelset() bool {
	l := make(map[uint64]struct{}, len(m))
	for _, ss := range m {
		hash := ss.Metric.Hash()
		if _, ok := l[hash]; ok {
			return true
		}
		l[hash] = struct{}{}
	}
	return false
}

// Result holds the resulting value of an execution or an error
// if any occurred.
type Result struct {
	Err      error
	Value    parser.Value
	Warnings storage.Warnings
}

// Vector returns a Vector if the result value is one. An error is returned if
// the result was an error or the result value is not a Vector.
func (r *Result) Vector() (Vector, error) {
	if r.Err != nil {
		return nil, r.Err
	}
	v, ok := r.Value.(Vector)
	if !ok {
		return nil, errors.New("query result is not a Vector")
	}
	return v, nil
}

// Matrix returns a Matrix. An error is returned if
// the result was an error or the result value is not a Matrix.
func (r *Result) Matrix() (Matrix, error) {
	if r.Err != nil {
		return nil, r.Err
	}
	v, ok := r.Value.(Matrix)
	if !ok {
		return nil, errors.New("query result is not a range Vector")
	}
	return v, nil
}

// Scalar returns a Scalar value. An error is returned if
// the result was an error or the result value is not a Scalar.
func (r *Result) Scalar() (Scalar, error) {
	if r.Err != nil {
		return Scalar{}, r.Err
	}
	v, ok := r.Value.(Scalar)
	if !ok {
		return Scalar{}, errors.New("query result is not a Scalar")
	}
	return v, nil
}

func (r *Result) String() string {
	if r.Err != nil {
		return r.Err.Error()
	}
	if r.Value == nil {
		return ""
	}
	return r.Value.String()
}

// StorageSeries simulates promql.Series as storage.Series.
type StorageSeries struct {
	series Series
}

// NewStorageSeries returns a StorageSeries from a Series.
func NewStorageSeries(series Series) *StorageSeries {
	return &StorageSeries{
		series: series,
	}
}

func (ss *StorageSeries) Labels() labels.Labels {
	return ss.series.Metric
}

// Iterator returns a new iterator of the data of the series.
func (ss *StorageSeries) Iterator() chunkenc.Iterator {
	return newStorageSeriesIterator(ss.series)
}

type storageSeriesIterator struct {
	points []Point
	curr   int
}

func newStorageSeriesIterator(series Series) *storageSeriesIterator {
	return &storageSeriesIterator{
		points: series.Points,
		curr:   -1,
	}
}

func (ssi *storageSeriesIterator) Seek(t int64) bool {
	i := ssi.curr
	if i < 0 {
		i = 0
	}
	for ; i < len(ssi.points); i++ {
		if ssi.points[i].T >= t {
			ssi.curr = i
			return true
		}
	}
	ssi.curr = len(ssi.points) - 1
	return false
}

func (ssi *storageSeriesIterator) At() (t int64, v float64) {
	p := ssi.points[ssi.curr]
	return p.T, p.V
}

func (ssi *storageSeriesIterator) Next() bool {
	ssi.curr++
	return ssi.curr < len(ssi.points)
}

func (ssi *storageSeriesIterator) Err() error {
	return nil
}
<|endoftext|>


<commit_before>package proto

import (
	"fmt"
	"time"

	"h12.me/kafka/common"
)

type GetTimeFunc func([]byte) (time.Time, error)

func (o *OffsetByTime) Search(cl common.Cluster, getTime GetTimeFunc) (int64, error) {
	earliest, err := (&OffsetByTime{
		Topic:     o.Topic,
		Partition: o.Partition,
		Time:      Earliest,
	}).Fetch(cl)
	if err != nil {
		return -1, err
	}
	if o.Time == Earliest {
		return earliest, nil
	}
	latest, err := (&OffsetByTime{
		Topic:     o.Topic,
		Partition: o.Partition,
		Time:      Latest,
	}).Fetch(cl)
	if err != nil {
		return -1, err
	}

	const maxMessageSize = 10000
	getter := timeGetter{
		Messages{
			Topic:       o.Topic,
			Partition:   o.Partition,
			MinBytes:    1,
			MaxBytes:    maxMessageSize,
			MaxWaitTime: 100 * time.Millisecond,
		},
		getTime,
	}
	min, max := earliest, latest
	mid := min + (max-min)/2
	for min <= max {
		mid = min + (max-min)/2
		midTime, err := getter.get(cl, mid)
		if err != nil {
			return -1, err
		}
		if midTime.Equal(o.Time) {
			break
		} else if midTime.Before(o.Time) {
			min = mid + 1
		} else {
			max = mid - 1
		}
	}
	return o.searchOffsetBefore(cl, earliest, mid, latest, getter)
}

func (o *OffsetByTime) searchOffsetBefore(cl common.Cluster, min, mid, max int64, getter timeGetter) (int64, error) {
	const maxJitter = 1000 // time may be interleaved in a small range
	midTime, err := getter.get(cl, mid)
	if err != nil {
		return -1, err
	}
	if midTime.Before(o.Time) {
		mid -= maxJitter
	} else {
		mid -= 2 * maxJitter // we have passed the point, go back with double jitter
	}
	if mid < min {
		mid = min
	}
	for offset := mid; offset <= max; offset++ {
		getter.Offset = offset
		messages, err := getter.Consume(cl)
		if err != nil {
			return -1, err
		}
		if len(messages) == 0 {
			return -1, fmt.Errorf("fail to search offset: zero message count")
		}
		for _, message := range messages {
			t, err := getter.getTime(message.Value())
			if err != nil {
				return -1, err
			}
			if t.After(o.Time) || t.Equal(o.Time) {
				return offset, nil
			}
			offset = message.Offset
		}
	}
	return mid, nil
}

type timeGetter struct {
	Messages
	getTime GetTimeFunc
}

func (g *timeGetter) get(cl common.Cluster, offset int64) (time.Time, error) {
	g.Offset = offset
	messages, err := g.Consume(cl)
	if err != nil {
		return time.Time{}, err
	}
	if len(messages) == 0 {
		return time.Time{}, fmt.Errorf("no messages at topic %s, partition %d, offset %d", g.Topic, g.Partition, g.Offset)
	}
	if messages[0].Offset != offset {
		return time.Time{}, fmt.Errorf("OFFSET MISMATCH!!! %d, %d", messages[0].Offset, offset)
	}
	return g.getTime(messages[0].Value())
}
<commit_msg>refactor<commit_after>package proto

import (
	"fmt"
	"time"

	"h12.me/kafka/common"
)

type GetTimeFunc func([]byte) (time.Time, error)

func (o *OffsetByTime) Search(cl common.Cluster, getTime GetTimeFunc) (int64, error) {
	earliest, err := (&OffsetByTime{
		Topic:     o.Topic,
		Partition: o.Partition,
		Time:      Earliest,
	}).Fetch(cl)
	if err != nil {
		return -1, err
	}
	if o.Time == Earliest {
		return earliest, nil
	}
	latest, err := (&OffsetByTime{
		Topic:     o.Topic,
		Partition: o.Partition,
		Time:      Latest,
	}).Fetch(cl)
	if err != nil {
		return -1, err
	}

	const maxMessageSize = 10000
	getter := timeGetter{
		Messages{
			Topic:       o.Topic,
			Partition:   o.Partition,
			MinBytes:    1,
			MaxBytes:    maxMessageSize,
			MaxWaitTime: 100 * time.Millisecond,
		},
		cl,
		getTime,
	}
	min, max := earliest, latest
	mid := min + (max-min)/2
	for min <= max {
		mid = min + (max-min)/2
		midTime, err := getter.get(mid)
		if err != nil {
			return -1, err
		}
		if midTime.Equal(o.Time) {
			break
		} else if midTime.Before(o.Time) {
			min = mid + 1
		} else {
			max = mid - 1
		}
	}
	return o.searchOffsetBefore(cl, earliest, mid, latest, getter)
}

func (o *OffsetByTime) searchOffsetBefore(cl common.Cluster, min, mid, max int64, getter timeGetter) (int64, error) {
	const maxJitter = 1000 // time may be interleaved in a small range
	midTime, err := getter.get(mid)
	if err != nil {
		return -1, err
	}
	if midTime.Before(o.Time) {
		mid -= maxJitter
	} else {
		mid -= 2 * maxJitter // we have passed the point, go back with double jitter
	}
	if mid < min {
		mid = min
	}
	for offset := mid; offset <= max; offset++ {
		getter.Offset = offset
		messages, err := getter.Consume(cl)
		if err != nil {
			return -1, err
		}
		if len(messages) == 0 {
			return -1, fmt.Errorf("fail to search offset: zero message count")
		}
		for _, message := range messages {
			t, err := getter.getTime(message.Value())
			if err != nil {
				return -1, err
			}
			if t.After(o.Time) || t.Equal(o.Time) {
				return offset, nil
			}
			offset = message.Offset
		}
	}
	return mid, nil
}

type timeGetter struct {
	Messages
	cl      common.Cluster
	getTime GetTimeFunc
}

func (g *timeGetter) get(offset int64) (time.Time, error) {
	g.Offset = offset
	messages, err := g.Consume(g.cl)
	if err != nil {
		return time.Time{}, err
	}
	if len(messages) == 0 {
		return time.Time{}, fmt.Errorf("no messages at topic %s, partition %d, offset %d", g.Topic, g.Partition, g.Offset)
	}
	if messages[0].Offset != offset {
		return time.Time{}, fmt.Errorf("OFFSET MISMATCH!!! %d, %d", messages[0].Offset, offset)
	}
	return g.getTime(messages[0].Value())
}
<|endoftext|>


<commit_before>package psh

import (
	"os/exec"
	"testing"
	"time"
)

func TestStateConsts(t *testing.T) {
	if !(UNSTARTED == 0 && RUNNING == 1 && FINISHED == 2 && PANICED == 3) {
		t.Fail()
	}
}

func TestPshExecBasic(t *testing.T) {
	cmdr := NewRunningCommand(
		exec.Command("echo"),
	)
	cmdr.startCalmly()
	cmdr.WaitSoon(1 * time.Second)
	cmdr.GetExitCode()
}
<commit_msg>check exit code in psh_test<commit_after>package psh

import (
	"github.com/coocood/assrt"
	"os/exec"
	"testing"
	"time"
)

func TestStateConsts(t *testing.T) {
	if !(UNSTARTED == 0 && RUNNING == 1 && FINISHED == 2 && PANICED == 3) {
		t.Fail()
	}
}

// Test that we can exec something, wait, and it returns quickly and with an exit code.
func TestPshExecBasic(t *testing.T) {
	assert := assrt.NewAssert(t)

	cmdr := NewRunningCommand(
		exec.Command("echo"),
	)
	cmdr.startCalmly()
	cmdr.WaitSoon(1 * time.Second)
	assert.Equal(
		0,
		cmdr.GetExitCode(),
	)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"image/png"
	"io/ioutil"
	"os"
	"path"
	"strings"

	"code.google.com/p/freetype-go/freetype"
)

func main() {
	args := os.Args
	if len(args) != 2 {
		fmt.Printf("usage:\t%v [path/to/sources]\n", args[0])
		return
	}

	dirPath := os.Args[1]

	err := gen(dirPath)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
}

func gen(dirPath string) error {
	captionFile, _, err := getSrcFiles(dirPath)
	if err != nil {
		return err
	}
	if captionFile == nil {
		return errors.New("No caption file found")
	}
	text, err := ioutil.ReadAll(captionFile)
	if err != nil {
		return err
	}

	img := image.NewNRGBA(image.Rect(0, 0, 1000, 1000))
	draw.Draw(img, image.Rect(0, 0, 1000, 1000), image.NewUniform(color.Black), image.Point{0, 0}, draw.Over)

	fontContext, err := createFontContext(img)
	if err != nil {
		return err
	}

	fontContext.DrawString(string(text), freetype.Pt(100, 100))
	writeImgToFile("out.png", img)

	return nil
}

func writeImgToFile(filename string, img image.Image) error {
	outimg, err := os.Create(filename)
	if err != nil {
		return errors.New(fmt.Sprint("Unable to open image for writing", err))
	}
	defer outimg.Close()

	err = png.Encode(outimg, img)
	if err != nil {
		return errors.New("Unable to write image")
	}
	return nil
}

func createFontContext(dst draw.Image) (fontContext *freetype.Context, err error) {
	fontContext = freetype.NewContext()

	fontBytes, err := ioutil.ReadFile("/Users/bariteau/Downloads/love_letter_tw/Lovelt__.ttf")
	if err != nil {
		err = errors.New("Unable to read font file")
		return
	}
	font, err := freetype.ParseFont(fontBytes)

	if err != nil {
		err = errors.New("Unable to parse font file")
		return
	}

	fontContext.SetFont(font)
	fontContext.SetFontSize(12.0)
	fontContext.SetSrc(image.NewUniform(color.White))
	fontContext.SetDst(dst)
	fontContext.SetClip(dst.Bounds())
	return
}

func getSrcFiles(dirPath string) (captions *os.File, pictures []*os.File, err error) {
	dir, err := os.Open(dirPath)
	if err != nil {
		return
	}

	dirInfo, err := dir.Stat()
	if err != nil {
		return
	}

	if !dirInfo.IsDir() {
		errors.New(fmt.Sprint("Not a directory: ", dirPath))
	}

	fileinfos, err := dir.Readdir(0)
	if err != nil {
		return
	}

	for _, fileinfo := range fileinfos {
		name := fileinfo.Name()
		if strings.HasPrefix(name, "caption") {
			if captions == nil {
				captions, err = os.Open(path.Join(dirPath, name))
				if err != nil {
					captions = nil
				}
			}
			continue
		}
	}
	return
}
<commit_msg>images ftw<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"image/png"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"

	"code.google.com/p/freetype-go/freetype"
)

func main() {
	args := os.Args
	if len(args) != 2 {
		fmt.Printf("usage:\t%v [path/to/sources]\n", args[0])
		return
	}

	dirPath := os.Args[1]

	img, err := gen(dirPath)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
	writeImgToFile("out.png", img)
}

func gen(dirPath string) (img draw.Image, err error) {
	captions, pictures, err := getSrcData(dirPath)
	if err != nil {
		return
	}
	if len(captions) == 0 {
		err = errors.New("No caption file found or empty caption file")
		return
	}
	text := captions[0]

	img = image.NewNRGBA(image.Rect(0, 0, 1000, 1000))
	draw.Draw(img, image.Rect(0, 0, 1000, 1000), image.NewUniform(color.Black), image.Point{0, 0}, draw.Over)
	draw.Draw(img, image.Rect(0, 0, 100, 100), pictures[0], image.Point{0, 0}, draw.Over)

	fontContext, err := createFontContext(img)
	if err != nil {
		return
	}

	fontContext.DrawString(string(text), freetype.Pt(100, 100))

	return
}

func writeImgToFile(filename string, img image.Image) error {
	outimg, err := os.Create(filename)
	if err != nil {
		return errors.New(fmt.Sprint("Unable to open image for writing", err))
	}
	defer outimg.Close()

	err = png.Encode(outimg, img)
	if err != nil {
		return errors.New("Unable to write image")
	}
	return nil
}

func createFontContext(dst draw.Image) (fontContext *freetype.Context, err error) {
	fontContext = freetype.NewContext()

	fontBytes, err := ioutil.ReadFile("/Users/bariteau/Downloads/love_letter_tw/Lovelt__.ttf")
	if err != nil {
		err = errors.New("Unable to read font file")
		return
	}
	font, err := freetype.ParseFont(fontBytes)

	if err != nil {
		err = errors.New("Unable to parse font file")
		return
	}

	fontContext.SetFont(font)
	fontContext.SetFontSize(12.0)
	fontContext.SetSrc(image.NewUniform(color.White))
	fontContext.SetDst(dst)
	fontContext.SetClip(dst.Bounds())
	return
}

func getSrcData(dirPath string) (captions []string, pictures []image.Image, err error) {
	captionFile, pictureFiles, err := getSrcFiles(dirPath)
	if err != nil {
		return
	}
	captionFileScanner := bufio.NewScanner(captionFile)
	for captionFileScanner.Scan() {
		captions = append(captions, captionFileScanner.Text())
	}
	err = captionFileScanner.Err()

	pictures = make([]image.Image, len(pictureFiles))
	for i, pictureFile := range pictureFiles {
		pictures[i], _, err = image.Decode(pictureFile)
		if err != nil {
			return
		}
	}
	return
}

var imgSrcFilenamesPattern = regexp.MustCompile("([1-3])[.](png|PNG)")

func getSrcFiles(dirPath string) (captions *os.File, pictures []*os.File, err error) {
	dir, err := os.Open(dirPath)
	if err != nil {
		return
	}

	dirInfo, err := dir.Stat()
	if err != nil {
		return
	}

	if !dirInfo.IsDir() {
		errors.New(fmt.Sprint("Not a directory: ", dirPath))
	}

	fileinfos, err := dir.Readdir(0)
	if err != nil {
		return
	}

	picturesFileMap := make(map[string]*os.File)
	for _, fileinfo := range fileinfos {
		name := fileinfo.Name()
		fmt.Println(name)
		if strings.HasPrefix(name, "caption") {
			if captions == nil {
				captions, err = os.Open(path.Join(dirPath, name))
				if err != nil {
					captions = nil
				}
			}
		} else if matches := imgSrcFilenamesPattern.FindStringSubmatch(name); matches != nil {
			file, err := os.Open(path.Join(dirPath, name))
			if err != nil {
				continue
			}
			picturesFileMap[matches[1]] = file
		}
	}

	pictures = make([]*os.File, len(picturesFileMap))
	for i := range pictures {
		pictures[i] = picturesFileMap[strconv.Itoa(i+1)]
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"strconv"

	"github.com/fatih/color"
	"github.com/leeola/fixity"
	"github.com/leeola/fixity/util/dyntabwriter"
	"github.com/urfave/cli"
)

const (
	blocktypeContent = "Content: "
)

func BlocksCmd(ctx *cli.Context) error {
	fixi, err := loadFixity(ctx)
	if err != nil {
		return err
	}

	w := dyntabwriter.New(os.Stdout)
	defer w.Flush()
	w.Header(" ", "BLOCK", "HASH", "TYPE", "CONTENT", "ID")

	b, err := fixi.Blockchain().Head()
	if err == fixity.ErrEmptyBlockchain {
		return nil
	}
	if err != nil {
		return err
	}

	c, err := b.Content()
	if err != nil {
		return err
	}

	showBlockHashes := ctx.Bool("block-hashes")
	showContentHashes := ctx.Bool("content-hashes")

	bHash := sumHash(b.Hash, showBlockHashes)
	cHash := sumHash(b.Hash, showContentHashes)

	w.Println(" ",
		color.GreenString(strconv.Itoa(b.Block)),
		color.GreenString(bHash),
		color.GreenString(blockType(b)),
		color.YellowString(cHash),
		color.YellowString(c.Id),
	)

	for i := 0; i < ctx.Int("limit")-1 && b.PreviousBlockHash != ""; i++ {
		b, err = b.PreviousBlock()
		if err != nil {
			return err
		}

		c, err = b.Content()
		if err != nil {
			return err
		}

		bHash := sumHash(b.Hash, showBlockHashes)
		cHash := sumHash(b.Hash, showContentHashes)

		w.Println(" ",
			color.GreenString(strconv.Itoa(b.Block)),
			color.GreenString(bHash),
			color.GreenString(blockType(b)),
			color.YellowString(cHash),
			color.YellowString(c.Id),
		)
	}

	return nil
}

func sumHash(h string, doNothing bool) string {
	if doNothing {
		return h
	}
	return h[:8]
}

func blockType(b fixity.Block) string {
	switch {
	case b.ContentHash != "":
		return "content"
	default:
		return "unknown"
	}
}
<commit_msg>fix: properly show content hash, not block hash<commit_after>package main

import (
	"os"
	"strconv"

	"github.com/fatih/color"
	"github.com/leeola/fixity"
	"github.com/leeola/fixity/util/dyntabwriter"
	"github.com/urfave/cli"
)

const (
	blocktypeContent = "Content: "
)

func BlocksCmd(ctx *cli.Context) error {
	fixi, err := loadFixity(ctx)
	if err != nil {
		return err
	}

	w := dyntabwriter.New(os.Stdout)
	defer w.Flush()
	w.Header(" ", "BLOCK", "HASH", "TYPE", "CONTENT", "ID")

	b, err := fixi.Blockchain().Head()
	if err == fixity.ErrEmptyBlockchain {
		return nil
	}
	if err != nil {
		return err
	}

	c, err := b.Content()
	if err != nil {
		return err
	}

	showBlockHashes := ctx.Bool("block-hashes")
	showContentHashes := ctx.Bool("content-hashes")

	bHash := sumHash(b.Hash, showBlockHashes)
	cHash := sumHash(c.Hash, showContentHashes)

	w.Println(" ",
		color.GreenString(strconv.Itoa(b.Block)),
		color.GreenString(bHash),
		color.GreenString(blockType(b)),
		color.YellowString(cHash),
		color.YellowString(c.Id),
	)

	for i := 0; i < ctx.Int("limit")-1 && b.PreviousBlockHash != ""; i++ {
		b, err = b.PreviousBlock()
		if err != nil {
			return err
		}

		c, err = b.Content()
		if err != nil {
			return err
		}

		bHash := sumHash(b.Hash, showBlockHashes)
		cHash := sumHash(c.Hash, showContentHashes)

		w.Println(" ",
			color.GreenString(strconv.Itoa(b.Block)),
			color.GreenString(bHash),
			color.GreenString(blockType(b)),
			color.YellowString(cHash),
			color.YellowString(c.Id),
		)
	}

	return nil
}

func sumHash(h string, doNothing bool) string {
	if doNothing {
		return h
	}
	return h[:8]
}

func blockType(b fixity.Block) string {
	switch {
	case b.ContentHash != "":
		return "content"
	default:
		return "unknown"
	}
}
<|endoftext|>


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

// This file implements access to gccgo-generated export data.

package main

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

	"code.google.com/p/go.tools/go/gccgoimporter"
	"code.google.com/p/go.tools/go/importer"
	"code.google.com/p/go.tools/go/types"
)

func init() {
	// importer for default gccgo
	var inst gccgoimporter.GccgoInstallation
	inst.InitFromDriver("gccgo")
	register("gccgo", inst.GetImporter(nil))

	// importer for gccgo using condensed export format (experimental)
	register("gccgo-new", getNewImporter(append(inst.SearchPaths(), ".")))
}

// This function is an adjusted variant of gccgoimporter.GccgoInstallation.GetImporter.
func getNewImporter(searchpaths []string) types.Importer {
	return func(imports map[string]*types.Package, pkgpath string) (pkg *types.Package, err error) {
		if pkgpath == "unsafe" {
			return types.Unsafe, nil
		}

		fpath, err := findExportFile(searchpaths, pkgpath)
		if err != nil {
			return
		}

		reader, closer, err := openExportFile(fpath)
		if err != nil {
			return nil, err
		}
		defer closer.Close()

		// TODO(gri) At the moment we just read the entire file.
		// We should change importer.ImportData to take an io.Reader instead.
		data, err := ioutil.ReadAll(reader)
		if err != nil && err != io.EOF {
			return nil, err
		}

		return importer.ImportData(packages, data)
	}
}

// This function is an exact copy of gccgoimporter.findExportFile.
func findExportFile(searchpaths []string, pkgpath string) (string, error) {
	for _, spath := range searchpaths {
		pkgfullpath := filepath.Join(spath, pkgpath)
		pkgdir, name := filepath.Split(pkgfullpath)

		for _, filepath := range [...]string{
			pkgfullpath,
			pkgfullpath + ".gox",
			pkgdir + "lib" + name + ".so",
			pkgdir + "lib" + name + ".a",
			pkgfullpath + ".o",
		} {
			fi, err := os.Stat(filepath)
			if err == nil && !fi.IsDir() {
				return filepath, nil
			}
		}
	}

	return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, strings.Join(searchpaths, ":"))
}

// This function is an exact copy of gccgoimporter.openExportFile.
func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) {
	f, err := os.Open(fpath)
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			f.Close()
		}
	}()
	closer = f

	var magic [4]byte
	_, err = f.ReadAt(magic[:], 0)
	if err != nil {
		return
	}

	if string(magic[:]) == "v1;\n" {
		// Raw export data.
		reader = f
		return
	}

	ef, err := elf.NewFile(f)
	if err != nil {
		return
	}

	sec := ef.Section(".go_export")
	if sec == nil {
		err = fmt.Errorf("%s: .go_export section not found", fpath)
		return
	}

	reader = sec.Open()
	return
}
<commit_msg>go.tools/cmd/godex: permit absolute paths for gccgo-generated package (export) files<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file implements access to gccgo-generated export data.

package main

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

	"code.google.com/p/go.tools/go/gccgoimporter"
	"code.google.com/p/go.tools/go/importer"
	"code.google.com/p/go.tools/go/types"
)

func init() {
	incpaths := []string{"/"}

	// importer for default gccgo
	var inst gccgoimporter.GccgoInstallation
	inst.InitFromDriver("gccgo")
	register("gccgo", inst.GetImporter(incpaths))

	// importer for gccgo using condensed export format (experimental)
	register("gccgo-new", getNewImporter(append(append(incpaths, inst.SearchPaths()...), ".")))
}

// This function is an adjusted variant of gccgoimporter.GccgoInstallation.GetImporter.
func getNewImporter(searchpaths []string) types.Importer {
	return func(imports map[string]*types.Package, pkgpath string) (pkg *types.Package, err error) {
		if pkgpath == "unsafe" {
			return types.Unsafe, nil
		}

		fpath, err := findExportFile(searchpaths, pkgpath)
		if err != nil {
			return
		}

		reader, closer, err := openExportFile(fpath)
		if err != nil {
			return nil, err
		}
		defer closer.Close()

		// TODO(gri) At the moment we just read the entire file.
		// We should change importer.ImportData to take an io.Reader instead.
		data, err := ioutil.ReadAll(reader)
		if err != nil && err != io.EOF {
			return nil, err
		}

		return importer.ImportData(packages, data)
	}
}

// This function is an exact copy of gccgoimporter.findExportFile.
func findExportFile(searchpaths []string, pkgpath string) (string, error) {
	for _, spath := range searchpaths {
		pkgfullpath := filepath.Join(spath, pkgpath)
		pkgdir, name := filepath.Split(pkgfullpath)

		for _, filepath := range [...]string{
			pkgfullpath,
			pkgfullpath + ".gox",
			pkgdir + "lib" + name + ".so",
			pkgdir + "lib" + name + ".a",
			pkgfullpath + ".o",
		} {
			println("trying", filepath)
			fi, err := os.Stat(filepath)
			if err == nil && !fi.IsDir() {
				return filepath, nil
			}
		}
	}

	return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, strings.Join(searchpaths, ":"))
}

// This function is an exact copy of gccgoimporter.openExportFile.
func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) {
	f, err := os.Open(fpath)
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			f.Close()
		}
	}()
	closer = f

	var magic [4]byte
	_, err = f.ReadAt(magic[:], 0)
	if err != nil {
		return
	}

	if string(magic[:]) == "v1;\n" {
		// Raw export data.
		reader = f
		return
	}

	ef, err := elf.NewFile(f)
	if err != nil {
		return
	}

	sec := ef.Section(".go_export")
	if sec == nil {
		err = fmt.Errorf("%s: .go_export section not found", fpath)
		return
	}

	reader = sec.Open()
	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"image"
	"image/png"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/musl/gofr/lib/gofr"
	"github.com/nfnt/resize"
)

// Version is a semantic version for the package.
const Version = "0.2.2"

var renderJobs = make(map[string]RenderJob)
var renderJobsMutex = &sync.Mutex{}

// RenderJob contains all information necessary to complete a Render.
type RenderJob struct {
	Parameters gofr.Parameters
	Cancel     chan bool
	Threads    int
}

// Render executes a RenderJob's unit of work.
func (rj *RenderJob) Render() (image.Image, error) {
	img := image.NewNRGBA64(image.Rect(0, 0, rj.Parameters.ImageWidth, rj.Parameters.ImageHeight))
	contexts := gofr.MakeContexts(img, rj.Threads, &rj.Parameters)

	err := gofr.Render(rj.Threads, contexts, rj.Cancel)
	if err != nil {
		return nil, err
	}

	image := resize.Resize(rj.Parameters.Width, rj.Parameters.Height, image.Image(img), resize.Lanczos3)
	return image, nil
}

// LogResponseWriter logs how long a response took and what it's
// resulting status code was.
type LogResponseWriter struct {
	http.ResponseWriter
	Status int
	Start  time.Time
	End    time.Time
}

// NewLogResponseWriter returns a new LogResponseWriter that wraps a
// given http.ResponseWriter.
func NewLogResponseWriter(w http.ResponseWriter) *LogResponseWriter {
	return &LogResponseWriter{w, 0, time.Now(), time.Now()}
}

// WriteHeader implements http.ResponseWriter
func (lrw *LogResponseWriter) WriteHeader(code int) {
	lrw.Status = code
	lrw.ResponseWriter.WriteHeader(code)
}

// Log writes out the time difference between being initialized and when
// it is called.
func (lrw LogResponseWriter) Log(message string) {
	lrw.End = time.Now()
	log.Printf("%s %v\n", message, lrw.End.Sub(lrw.Start))
}

func wrapHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		lrw := NewLogResponseWriter(w)

		lrw.Log(fmt.Sprintf("%s -> %s %s", r.Method, r.URL.Path, r.RemoteAddr))
		h.ServeHTTP(lrw, r)
		lrw.Log(fmt.Sprintf("%d <- %s %s %s", lrw.Status, r.Method, r.URL.Path, r.RemoteAddr))
	})
}

func wrapHandlerFunc(h http.HandlerFunc) http.Handler {
	return wrapHandler(http.Handler(h))
}

func finish(w http.ResponseWriter, status int, message string) {
	w.WriteHeader(status)
	io.WriteString(w, message)
}

func makeSPARoute(docroot, index string) http.HandlerFunc {

	// Rule: routes must end in '/', files must not. This means that
	// requests for directories will always be served with the spa index
	// file.
	pattern := regexp.MustCompile(`/$`)

	return func(w http.ResponseWriter, r *http.Request) {
		var reqPath string

		if r.URL.Path == "/" || pattern.MatchString(r.URL.Path) {
			reqPath = path.Join(docroot, index)
		} else {
			reqPath = filepath.Clean(r.URL.Path)
			reqPath = path.Join(docroot, reqPath)
		}

		_, err := os.Stat(reqPath)
		if err != nil {
			finish(w, 404, "File Not Found")
			return
		}

		f, err := os.Open(reqPath)
		defer f.Close()
		if err != nil {
			finish(w, 500, fmt.Sprintf("Unable to open file: %s", reqPath))
			return
		}

		i, err := os.Stat(reqPath)
		if err != nil {
			finish(w, 500, fmt.Sprintf("Unable to stat file: %s", reqPath))
			return
		}

		http.ServeContent(w, r, reqPath, i.ModTime(), f)
	}
}

func routePNG(w http.ResponseWriter, r *http.Request) {
	id := uuid.New()

	// TODO either check a table for currently rendering IDs and return
	// HTTP 429 (one request per id in flight), or cancel the existing,
	// running request and continue this one.

	if r.Method != "GET" {
		finish(w, http.StatusMethodNotAllowed, "Method not allowed.")
		return
	}

	q := r.URL.Query()

	s, err := strconv.Atoi(q.Get("s"))
	if err != nil {
		s = 1
	}

	e, err := strconv.Atoi(q.Get("p"))
	if err != nil {
		e = 1
	}

	width, err := strconv.Atoi(q.Get("w"))
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid width")
		return
	}

	height, err := strconv.Atoi(q.Get("h"))
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid height")
		return
	}

	iterations, err := strconv.Atoi(q.Get("i"))
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid i")
		return
	}

	er, err := strconv.ParseFloat(q.Get("e"), 64)
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid e")
		return
	}

	rmin, err := strconv.ParseFloat(q.Get("rmin"), 64)
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid rmin")
		return
	}

	imin, err := strconv.ParseFloat(q.Get("imin"), 64)
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid rmin")
		return
	}

	rmax, err := strconv.ParseFloat(q.Get("rmax"), 64)
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid rmax")
		return
	}

	imax, err := strconv.ParseFloat(q.Get("imax"), 64)
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid rmin")
		return
	}

	j := RenderJob{
		Parameters: gofr.Parameters{
			Width:        uint(width),
			Height:       uint(height),
			ImageWidth:   width * s,
			ImageHeight:  height * s,
			MaxI:         iterations,
			EscapeRadius: er,
			Min:          complex(rmin, imin),
			Max:          complex(rmax, imax),
			RenderFunc:   q.Get("r"),
			ColorFunc:    q.Get("c"),
			MemberColor:  q.Get("m"),
			Power:        e,
		},
		Threads: runtime.NumCPU(),
		Cancel:  make(chan bool),
	}

	renderID := q.Get("render-id")
	if renderID == "" {
		finish(w, http.StatusUnprocessableEntity, "Missing render-id")
		return
	}

	defer func() {
		renderJobsMutex.Lock()
		delete(renderJobs, renderID)
		renderJobsMutex.Unlock()
	}()

	renderJobsMutex.Lock()
	if _, exists := renderJobs[renderID]; exists {
		close(renderJobs[renderID].Cancel)
		delete(renderJobs, renderID)
	}
	renderJobs[renderID] = j
	renderJobsMutex.Unlock()

	image, err := j.Render()
	if err != nil {
		finish(w, http.StatusTooManyRequests, err.Error())
		return
	}

	w.Header().Set("Content-Type", "image/png")
	w.Header().Set("X-Render-Job-ID", id.String())
	w.WriteHeader(http.StatusOK)
	png.Encode(w, image)
}

func routeStatus(w http.ResponseWriter, r *http.Request) {
	finish(w, http.StatusOK, "OK")
}

func main() {
	var value string

	log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.LUTC)
	log.Printf("gofrd v%s", Version)
	log.Printf("libgofrd v%s", gofr.Version)

	staticDir := "./build"
	if value = os.Getenv("GOFR_STATIC_DIR"); value != "" {
		staticDir = value
	}
	staticDir, err := filepath.Abs(staticDir)
	if err != nil {
		panic(err)
	}
	log.Printf("Serving from: %s\n", staticDir)

	bindAddr := "0.0.0.0:8000"
	if value = os.Getenv("GOFR_BIND_ADDR"); value != "" {
		bindAddr = value
	}
	log.Printf("Listening on: %s\n", bindAddr)

	//http.Handle("/", wrapHandler(http.FileServer(http.Dir(staticDir))))
	http.Handle("/", wrapHandlerFunc(makeSPARoute(staticDir, "index.html")))
	http.Handle("/png", wrapHandlerFunc(routePNG))
	http.Handle("/status", wrapHandlerFunc(routeStatus))

	/* Run the thing. */
	log.Fatal(http.ListenAndServe(bindAddr, nil))
}
<commit_msg>Simplify SPA route handler, comments, error handling<commit_after>package main

import (
	"fmt"
	"image"
	"image/png"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strconv"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/musl/gofr/lib/gofr"
	"github.com/nfnt/resize"
)

// Version is a semantic version for the package.
const Version = "0.2.2"

var renderJobs = make(map[string]RenderJob)
var renderJobsMutex = &sync.Mutex{}

// RenderJob contains all information necessary to complete a Render.
type RenderJob struct {
	Parameters gofr.Parameters
	Cancel     chan bool
	Threads    int
}

// Render executes a RenderJob's unit of work.
func (rj *RenderJob) Render() (image.Image, error) {
	img := image.NewNRGBA64(image.Rect(0, 0, rj.Parameters.ImageWidth, rj.Parameters.ImageHeight))
	contexts := gofr.MakeContexts(img, rj.Threads, &rj.Parameters)

	err := gofr.Render(rj.Threads, contexts, rj.Cancel)
	if err != nil {
		return nil, err
	}

	image := resize.Resize(rj.Parameters.Width, rj.Parameters.Height, image.Image(img), resize.Lanczos3)
	return image, nil
}

// LogResponseWriter logs how long a response took and what it's
// resulting status code was.
type LogResponseWriter struct {
	http.ResponseWriter
	Status int
	Start  time.Time
	End    time.Time
}

// NewLogResponseWriter returns a new LogResponseWriter that wraps a
// given http.ResponseWriter.
func NewLogResponseWriter(w http.ResponseWriter) *LogResponseWriter {
	return &LogResponseWriter{w, 0, time.Now(), time.Now()}
}

// WriteHeader implements http.ResponseWriter
func (lrw *LogResponseWriter) WriteHeader(code int) {
	lrw.Status = code
	lrw.ResponseWriter.WriteHeader(code)
}

// Log writes out the time difference between being initialized and when
// it is called.
func (lrw LogResponseWriter) Log(message string) {
	lrw.End = time.Now()
	log.Printf("%s %v\n", message, lrw.End.Sub(lrw.Start))
}

func wrapHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		lrw := NewLogResponseWriter(w)

		lrw.Log(fmt.Sprintf("%s -> %s %s", r.Method, r.URL.Path, r.RemoteAddr))
		h.ServeHTTP(lrw, r)
		lrw.Log(fmt.Sprintf("%d <- %s %s %s", lrw.Status, r.Method, r.URL.Path, r.RemoteAddr))
	})
}

func wrapHandlerFunc(h http.HandlerFunc) http.Handler {
	return wrapHandler(http.Handler(h))
}

func finish(w http.ResponseWriter, status int, message string) {
	l, err := io.WriteString(w, message)
	if err != nil || l != len(message) {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.WriteHeader(status)
}

func makeSPARoute(docroot string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		reqPath := filepath.Clean(r.URL.Path)
		reqPath = path.Join(docroot, reqPath)

		http.ServeFile(w, r, reqPath)
	}
}

func routePNG(w http.ResponseWriter, r *http.Request) {
	id := uuid.New()

	// TODO either check a table for currently rendering IDs and return
	// HTTP 429 (one request per id in flight), or cancel the existing,
	// running request and continue this one.

	if r.Method != "GET" {
		finish(w, http.StatusMethodNotAllowed, "Method not allowed.")
		return
	}

	q := r.URL.Query()

	s, err := strconv.Atoi(q.Get("s"))
	if err != nil {
		s = 1
	}

	e, err := strconv.Atoi(q.Get("p"))
	if err != nil {
		e = 1
	}

	width, err := strconv.Atoi(q.Get("w"))
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid width")
		return
	}

	height, err := strconv.Atoi(q.Get("h"))
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid height")
		return
	}

	iterations, err := strconv.Atoi(q.Get("i"))
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid i")
		return
	}

	er, err := strconv.ParseFloat(q.Get("e"), 64)
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid e")
		return
	}

	rmin, err := strconv.ParseFloat(q.Get("rmin"), 64)
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid rmin")
		return
	}

	imin, err := strconv.ParseFloat(q.Get("imin"), 64)
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid rmin")
		return
	}

	rmax, err := strconv.ParseFloat(q.Get("rmax"), 64)
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid rmax")
		return
	}

	imax, err := strconv.ParseFloat(q.Get("imax"), 64)
	if err != nil {
		finish(w, http.StatusUnprocessableEntity, "Invalid rmin")
		return
	}

	j := RenderJob{
		Parameters: gofr.Parameters{
			Width:        uint(width),
			Height:       uint(height),
			ImageWidth:   width * s,
			ImageHeight:  height * s,
			MaxI:         iterations,
			EscapeRadius: er,
			Min:          complex(rmin, imin),
			Max:          complex(rmax, imax),
			RenderFunc:   q.Get("r"),
			ColorFunc:    q.Get("c"),
			MemberColor:  q.Get("m"),
			Power:        e,
		},
		Threads: runtime.NumCPU(),
		Cancel:  make(chan bool),
	}

	renderID := q.Get("render-id")
	if renderID == "" {
		finish(w, http.StatusUnprocessableEntity, "Missing render-id")
		return
	}

	defer func() {
		renderJobsMutex.Lock()
		delete(renderJobs, renderID)
		renderJobsMutex.Unlock()
	}()

	renderJobsMutex.Lock()
	if _, exists := renderJobs[renderID]; exists {
		close(renderJobs[renderID].Cancel)
		delete(renderJobs, renderID)
	}
	renderJobs[renderID] = j
	renderJobsMutex.Unlock()

	image, err := j.Render()
	if err != nil {
		finish(w, http.StatusTooManyRequests, err.Error())
		return
	}

	err = png.Encode(w, image)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "image/png")
	w.Header().Set("X-Render-Job-ID", id.String())
	w.WriteHeader(http.StatusOK)
}

func routeStatus(w http.ResponseWriter, r *http.Request) {
	finish(w, http.StatusOK, "OK")
}

func main() {
	var value string

	log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.LUTC)
	log.Printf("gofrd v%s", Version)
	log.Printf("libgofrd v%s", gofr.Version)

	staticDir := "./build"
	if value = os.Getenv("GOFR_STATIC_DIR"); value != "" {
		staticDir = value
	}
	staticDir, err := filepath.Abs(staticDir)
	if err != nil {
		panic(err)
	}
	log.Printf("Serving from: %s\n", staticDir)

	bindAddr := "0.0.0.0:8000"
	if value = os.Getenv("GOFR_BIND_ADDR"); value != "" {
		bindAddr = value
	}
	log.Printf("Listening on: %s\n", bindAddr)

	http.Handle("/", wrapHandlerFunc(makeSPARoute(staticDir)))
	http.Handle("/png", wrapHandlerFunc(routePNG))
	http.Handle("/status", wrapHandlerFunc(routeStatus))

	/* Run the thing. */
	log.Fatal(http.ListenAndServe(bindAddr, nil))
}
<|endoftext|>