<commit_before>package chaoskube

import (
	"errors"
	"fmt"
	"math/rand"
	"time"

	log "github.com/sirupsen/logrus"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/selection"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api/v1"
)

// Chaoskube represents an instance of chaoskube
type Chaoskube struct {
	// a kubernetes client object
	Client kubernetes.Interface
	// a label selector which restricts the pods to choose from
	Labels labels.Selector
	// an annotation selector which restricts the pods to choose from
	Annotations labels.Selector
	// a namespace selector which restricts the pods to choose from
	Namespaces labels.Selector
	// a list of weekdays when termination is suspended
	ExcludedWeekdays []time.Weekday
	// the timezone to apply when detecting the current weekday
	Timezone *time.Location
	// an instance of logrus.StdLogger to write log messages to
	Logger log.StdLogger
	// dry run will not allow any pod terminations
	DryRun bool
	// seed value for the randomizer
	Seed int64
	// a function to retrieve the current time
	Now func() time.Time
}

// ErrPodNotFound is returned when no victim could be found
var ErrPodNotFound = errors.New("pod not found")

// msgVictimNotFound is the log message when no victim was found
var msgVictimNotFound = "No victim could be found. If that's surprising double-check your selectors."

// msgWeekdayExcluded is the log message when termination is suspended due to the weekday filter
var msgWeekdayExcluded = "This day of the week is excluded from chaos."

// New returns a new instance of Chaoskube. It expects a kubernetes client, a
// label and namespace selector to reduce the amount of affected pods as well as
// whether to enable dryRun mode and a seed to seed the randomizer with.
func New(client kubernetes.Interface, labels, annotations, namespaces labels.Selector, excludedWeekdays []time.Weekday, timezone *time.Location, logger log.StdLogger, dryRun bool, seed int64) *Chaoskube {
	c := &Chaoskube{
		Client:           client,
		Labels:           labels,
		Annotations:      annotations,
		Namespaces:       namespaces,
		ExcludedWeekdays: excludedWeekdays,
		Timezone:         timezone,
		Logger:           logger,
		DryRun:           dryRun,
		Seed:             seed,
		Now:              time.Now,
	}

	rand.Seed(c.Seed)

	return c
}

// Candidates returns the list of pods that are available for termination.
// It returns all pods matching the label selector and at least one namespace.
func (c *Chaoskube) Candidates() ([]v1.Pod, error) {
	listOptions := metav1.ListOptions{LabelSelector: c.Labels.String()}

	podList, err := c.Client.Core().Pods(v1.NamespaceAll).List(listOptions)
	if err != nil {
		return nil, err
	}

	pods, err := filterByNamespaces(podList.Items, c.Namespaces)
	if err != nil {
		return nil, err
	}

	pods, err = filterByAnnotations(pods, c.Annotations)
	if err != nil {
		return nil, err
	}

	return pods, nil
}

// Victim returns a random pod from the list of Candidates.
func (c *Chaoskube) Victim() (v1.Pod, error) {
	pods, err := c.Candidates()
	if err != nil {
		return v1.Pod{}, err
	}

	if len(pods) == 0 {
		return v1.Pod{}, ErrPodNotFound
	}

	index := rand.Intn(len(pods))

	return pods[index], nil
}

// DeletePod deletes the passed in pod iff dry run mode is enabled.
func (c *Chaoskube) DeletePod(victim v1.Pod) error {
	c.Logger.Printf("Killing pod %s/%s", victim.Namespace, victim.Name)

	if c.DryRun {
		return nil
	}

	return c.Client.Core().Pods(victim.Namespace).Delete(victim.Name, nil)
}

// TerminateVictim picks and deletes a victim if found.
func (c *Chaoskube) TerminateVictim() error {
	for _, wd := range c.ExcludedWeekdays {
		if wd == c.Now().In(c.Timezone).Weekday() {
			c.Logger.Printf(msgWeekdayExcluded)
			return nil
		}
	}

	victim, err := c.Victim()
	if err == ErrPodNotFound {
		c.Logger.Printf(msgVictimNotFound)
		return nil
	}
	if err != nil {
		return err
	}

	return c.DeletePod(victim)
}

// filterByNamespaces filters a list of pods by a given namespace selector.
func filterByNamespaces(pods []v1.Pod, namespaces labels.Selector) ([]v1.Pod, error) {
	// empty filter returns original list
	if namespaces.Empty() {
		return pods, nil
	}

	filteredList := []v1.Pod{}

	// split requirements into including and excluding groups
	reqs, _ := namespaces.Requirements()
	reqIncl := []labels.Requirement{}
	reqExcl := []labels.Requirement{}

	for _, req := range reqs {
		switch req.Operator() {
		case selection.Exists:
			reqIncl = append(reqIncl, req)
		case selection.DoesNotExist:
			reqExcl = append(reqExcl, req)
		default:
			return filteredList, fmt.Errorf("unsupported operator: %s", req.Operator())
		}
	}

	for _, pod := range pods {
		// if there aren't any including requirements, we're in by default
		included := len(reqIncl) == 0

		// convert the pod's namespace to an equivalent label selector
		selector := labels.Set{pod.Namespace: ""}

		// include pod if one including requirement matches
		for _, req := range reqIncl {
			if req.Matches(selector) {
				included = true
				break
			}
		}

		// exclude pod if it is filtered out by at least one excluding requirement
		for _, req := range reqExcl {
			if !req.Matches(selector) {
				included = false
				break
			}
		}

		if included {
			filteredList = append(filteredList, pod)
		}
	}

	return filteredList, nil
}

// filterByAnnotations filters a list of pods by a given annotation selector.
func filterByAnnotations(pods []v1.Pod, annotations labels.Selector) ([]v1.Pod, error) {
	// empty filter returns original list
	if annotations.Empty() {
		return pods, nil
	}

	filteredList := []v1.Pod{}

	for _, pod := range pods {
		// convert the pod's annotations to an equivalent label selector
		selector := labels.Set(pod.Annotations)

		// include pod if its annotations match the selector
		if annotations.Matches(selector) {
			filteredList = append(filteredList, pod)
		}
	}

	return filteredList, nil
}
<commit_msg>docs: update comment for New b/c it takes more params<commit_after>package chaoskube

import (
	"errors"
	"fmt"
	"math/rand"
	"time"

	log "github.com/sirupsen/logrus"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/selection"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api/v1"
)

// Chaoskube represents an instance of chaoskube
type Chaoskube struct {
	// a kubernetes client object
	Client kubernetes.Interface
	// a label selector which restricts the pods to choose from
	Labels labels.Selector
	// an annotation selector which restricts the pods to choose from
	Annotations labels.Selector
	// a namespace selector which restricts the pods to choose from
	Namespaces labels.Selector
	// a list of weekdays when termination is suspended
	ExcludedWeekdays []time.Weekday
	// the timezone to apply when detecting the current weekday
	Timezone *time.Location
	// an instance of logrus.StdLogger to write log messages to
	Logger log.StdLogger
	// dry run will not allow any pod terminations
	DryRun bool
	// seed value for the randomizer
	Seed int64
	// a function to retrieve the current time
	Now func() time.Time
}

// ErrPodNotFound is returned when no victim could be found
var ErrPodNotFound = errors.New("pod not found")

// msgVictimNotFound is the log message when no victim was found
var msgVictimNotFound = "No victim could be found. If that's surprising double-check your selectors."

// msgWeekdayExcluded is the log message when termination is suspended due to the weekday filter
var msgWeekdayExcluded = "This day of the week is excluded from chaos."

// New returns a new instance of Chaoskube. It expects a kubernetes client, a
// label, annotation and/or namespace selector to reduce the amount of affected
// pods as well as whether to enable dryRun mode and a seed to seed the randomizer
// with. You can also provide a list of weekdays and corresponding time zone when
// chaoskube should be inactive.
func New(client kubernetes.Interface, labels, annotations, namespaces labels.Selector, excludedWeekdays []time.Weekday, timezone *time.Location, logger log.StdLogger, dryRun bool, seed int64) *Chaoskube {
	c := &Chaoskube{
		Client:           client,
		Labels:           labels,
		Annotations:      annotations,
		Namespaces:       namespaces,
		ExcludedWeekdays: excludedWeekdays,
		Timezone:         timezone,
		Logger:           logger,
		DryRun:           dryRun,
		Seed:             seed,
		Now:              time.Now,
	}

	rand.Seed(c.Seed)

	return c
}

// Candidates returns the list of pods that are available for termination.
// It returns all pods matching the label selector and at least one namespace.
func (c *Chaoskube) Candidates() ([]v1.Pod, error) {
	listOptions := metav1.ListOptions{LabelSelector: c.Labels.String()}

	podList, err := c.Client.Core().Pods(v1.NamespaceAll).List(listOptions)
	if err != nil {
		return nil, err
	}

	pods, err := filterByNamespaces(podList.Items, c.Namespaces)
	if err != nil {
		return nil, err
	}

	pods, err = filterByAnnotations(pods, c.Annotations)
	if err != nil {
		return nil, err
	}

	return pods, nil
}

// Victim returns a random pod from the list of Candidates.
func (c *Chaoskube) Victim() (v1.Pod, error) {
	pods, err := c.Candidates()
	if err != nil {
		return v1.Pod{}, err
	}

	if len(pods) == 0 {
		return v1.Pod{}, ErrPodNotFound
	}

	index := rand.Intn(len(pods))

	return pods[index], nil
}

// DeletePod deletes the passed in pod iff dry run mode is enabled.
func (c *Chaoskube) DeletePod(victim v1.Pod) error {
	c.Logger.Printf("Killing pod %s/%s", victim.Namespace, victim.Name)

	if c.DryRun {
		return nil
	}

	return c.Client.Core().Pods(victim.Namespace).Delete(victim.Name, nil)
}

// TerminateVictim picks and deletes a victim if found.
func (c *Chaoskube) TerminateVictim() error {
	for _, wd := range c.ExcludedWeekdays {
		if wd == c.Now().In(c.Timezone).Weekday() {
			c.Logger.Printf(msgWeekdayExcluded)
			return nil
		}
	}

	victim, err := c.Victim()
	if err == ErrPodNotFound {
		c.Logger.Printf(msgVictimNotFound)
		return nil
	}
	if err != nil {
		return err
	}

	return c.DeletePod(victim)
}

// filterByNamespaces filters a list of pods by a given namespace selector.
func filterByNamespaces(pods []v1.Pod, namespaces labels.Selector) ([]v1.Pod, error) {
	// empty filter returns original list
	if namespaces.Empty() {
		return pods, nil
	}

	filteredList := []v1.Pod{}

	// split requirements into including and excluding groups
	reqs, _ := namespaces.Requirements()
	reqIncl := []labels.Requirement{}
	reqExcl := []labels.Requirement{}

	for _, req := range reqs {
		switch req.Operator() {
		case selection.Exists:
			reqIncl = append(reqIncl, req)
		case selection.DoesNotExist:
			reqExcl = append(reqExcl, req)
		default:
			return filteredList, fmt.Errorf("unsupported operator: %s", req.Operator())
		}
	}

	for _, pod := range pods {
		// if there aren't any including requirements, we're in by default
		included := len(reqIncl) == 0

		// convert the pod's namespace to an equivalent label selector
		selector := labels.Set{pod.Namespace: ""}

		// include pod if one including requirement matches
		for _, req := range reqIncl {
			if req.Matches(selector) {
				included = true
				break
			}
		}

		// exclude pod if it is filtered out by at least one excluding requirement
		for _, req := range reqExcl {
			if !req.Matches(selector) {
				included = false
				break
			}
		}

		if included {
			filteredList = append(filteredList, pod)
		}
	}

	return filteredList, nil
}

// filterByAnnotations filters a list of pods by a given annotation selector.
func filterByAnnotations(pods []v1.Pod, annotations labels.Selector) ([]v1.Pod, error) {
	// empty filter returns original list
	if annotations.Empty() {
		return pods, nil
	}

	filteredList := []v1.Pod{}

	for _, pod := range pods {
		// convert the pod's annotations to an equivalent label selector
		selector := labels.Set(pod.Annotations)

		// include pod if its annotations match the selector
		if annotations.Matches(selector) {
			filteredList = append(filteredList, pod)
		}
	}

	return filteredList, nil
}
<|endoftext|>


<commit_before>/*
*  Copyright (c) 2012 by Christoph Hack <christoph@tux21b.org>
*                        Stefan Lendl <st.lendl@gmail.com>
*  All rights reserved. Distributed under the Simplified BSD License.
*/

package main

import (
	"fmt"
	irc "github.com/fluffle/goirc/client"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"
)

var (
	timezone   = time.UTC
	wolframURL = "http://www.wolframalpha.com/input/?i=%s"
    mathbinPreviewURL = "http://mathbin.net/preview.cgi?body=%s"
    mathbinURL = "http://mathbin.net/%s"
    reMathbin = regexp.MustCompile(`equation_previews/[\d_]+\.png`)
)

func init() {
	tz, err := time.LoadLocation("Europe/Vienna")
	if err != nil {
		log.Fatal(err)
	}
	timezone = tz
}

var tmpl = template.Must(template.New("weierbot").Parse(`<!doctype html>

<title>{{.Title}} - Weierbot</title>

{{if .Prev}}Previous{{end}}
{{if .Next}}Next{{end}}

Weierbot

{{.Title}}

{{.Chatlog}}

`))

var reURL = regexp.MustCompile(`https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;]*[-A-Za-z0-9+&@#/%=~_|]`)
var reTime = regexp.MustCompile(`(?m)^\[\S+\]`)

type WeierBot struct {
	server     string
	client     *irc.Conn
	log        chan Message
	disconnect chan bool
}

func NewWeierBot(server, nick, password string, channels []string) *WeierBot {
	w := &WeierBot{
		server:     server,
		client:     irc.SimpleClient(nick),
		log:        make(chan Message, 512),
		disconnect: make(chan bool),
	}
	w.client.Me.Name = nick
	w.client.Me.Ident = nick

	w.client.AddHandler("connected", func(conn *irc.Conn, line *irc.Line) {
		conn.Privmsg("NickServ", "identify "+password)
		for _, ch := range channels {
			log.Printf("Joining %s\n", ch)
			conn.Join(ch)
		}
	})
	w.client.AddHandler("disconnected", func(conn *irc.Conn, line *irc.Line) {
		w.disconnect <- true
	})
	w.client.AddHandler("join", func(conn *irc.Conn, line *irc.Line) {
		if len(line.Args) >= 1 && strings.HasPrefix(line.Args[0], "#") {
			conn.Privmsg(line.Nick, fmt.Sprintf("Hallo %s, willkommen auf %s. "+
				"Dieser Channel wird unter http://weierbot.tux21b.org/ mitgespeichert.",
				line.Nick, line.Args[0]))
		}
	})
	w.client.AddHandler("privmsg", func(conn *irc.Conn, line *irc.Line) {
		if len(line.Args) < 2 {
			return
		}
		w.handleMessage(line.Args[0], Message{
			Nick:    line.Nick,
			Time:    line.Time,
			Message: line.Args[1],
		})
	})

	go w.writeLog()

	return w
}

func createMathbinImage(latexcode string) string {
    resp, err := http.Get(fmt.Sprintf(mathbinPreviewURL, url.QueryEscape("[EQ]"+latexcode+"[/EQ]")))
    if err != nil {
        return "error"
    }

    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)

    result := reMathbin.FindString(string(body))

    return fmt.Sprintf(mathbinURL, result)
}

func (bot *WeierBot) handleMessage(target string, msg Message) {
	if strings.HasPrefix(target, "#") {
		bot.log <- msg
	} else {
		target = msg.Nick
	}

	switch {
	case strings.HasPrefix(msg.Message, "!wolfram "):
		bot.send(target, fmt.Sprintf(wolframURL, url.QueryEscape(msg.Message[9:])))
	case msg.Message == "!log":
		bot.send(target, "http://weierbot.tux21b.org/")
    case strings.HasPrefix(msg.Message, "!mathbin "):
        imgurl := createMathbinImage(msg.Message[9:])
        bot.send(target, fmt.Sprintf(imgurl))
	}
}

func (bot *WeierBot) send(target, message string) {
	bot.client.Privmsg(target, message)
	if strings.HasPrefix(target, "#") {
		bot.log <- Message{Nick: bot.client.Me.Nick, Time: time.Now(),
			Message: message}
	}
}

func (bot *WeierBot) writeLog() {
	var (
		file     *os.File
		format   = "2006-01-02.log"
		filename string
	)
	defer func() {
		if file != nil {
			file.Close()
		}
	}()
	for msg := range bot.log {
		msg.Time = msg.Time.In(timezone)
		current := msg.Time.Format(format)
		if file == nil || current != filename {
			if file != nil {
				file.Close()
			}
			f, err := os.OpenFile(current,
				os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
			if err != nil {
				log.Println(err)
			} else {
				file, filename = f, current
			}
		}
		fmt.Fprintf(file, "%s\n", msg)
	}
}

func (bot *WeierBot) ServeIRC() {
	tries := 0
	for {
		tries++
		if err := bot.client.Connect(bot.server); err != nil {
			log.Println(err)
			if tries >= 5 {
				log.Fatal("maximum number of tries exceeded")
			}
			<-time.After(5 * time.Second)
			continue
		}
		tries = 0
		log.Printf("Connected to %s\n", bot.server)
		<-bot.disconnect
	}
}

func (bot *WeierBot) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	files, err := filepath.Glob("*.log")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	sort.Strings(files)

	if r.URL.Path == "/" && len(files) > 0 {
		http.Redirect(w, r, "/"+filepath.Base(files[len(files)-1]),
			http.StatusTemporaryRedirect)
	}
	index := sort.SearchStrings(files, r.URL.Path[1:])
	if index < 0 || index >= len(files) {
		http.NotFound(w, r)
		return
	}

	chatlog, err := ioutil.ReadFile(files[index])
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	chatlog = []byte(template.HTMLEscapeString(string(chatlog)))
	chatlog = reURL.ReplaceAllFunc(chatlog, func(m []byte) []byte {
		return []byte(fmt.Sprintf("%s", m, m))
	})
	chatlog = reTime.ReplaceAllFunc(chatlog, func(m []byte) []byte {
		return []byte(fmt.Sprintf("%s", m))
	})

	data := struct {
		Title      string
		Prev, Next string
		Chatlog    template.HTML
	}{
		Title:   filepath.Base(files[index]),
		Chatlog: template.HTML(chatlog),
	}
	if index > 0 {
		data.Prev = files[index-1]
	}
	if index < len(files)-1 {
		data.Next = files[index+1]
	}
	if err := tmpl.Execute(w, data); err != nil {
		log.Println(err.Error())
	}
}

type Message struct {
	Time    time.Time
	Nick    string
	Message string
}

func (m Message) String() string {
	return fmt.Sprintf("[%s] %s: %s", m.Time.Format(time.Kitchen), m.Nick, m.Message)
}

func main() {
	bot := NewWeierBot("irc.euirc.net", "weierbot2", "secret", []string{"#tm-test"})

	go func() {
		if err := http.ListenAndServe(":8005", bot); err != nil {
			log.Fatal(err)
		}
	}()

	bot.ServeIRC()
}
<commit_msg>go fmt<commit_after>/*
*  Copyright (c) 2012 by Christoph Hack <christoph@tux21b.org>
*                        Stefan Lendl <st.lendl@gmail.com>
*  All rights reserved. Distributed under the Simplified BSD License.
 */

package main

import (
	"fmt"
	irc "github.com/fluffle/goirc/client"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"
)

var (
	timezone          = time.UTC
	wolframURL        = "http://www.wolframalpha.com/input/?i=%s"
	mathbinPreviewURL = "http://mathbin.net/preview.cgi?body=%s"
	mathbinURL        = "http://mathbin.net/%s"
	reMathbin         = regexp.MustCompile(`equation_previews/[\d_]+\.png`)
)

func init() {
	tz, err := time.LoadLocation("Europe/Vienna")
	if err != nil {
		log.Fatal(err)
	}
	timezone = tz
}

var tmpl = template.Must(template.New("weierbot").Parse(`<!doctype html>

<title>{{.Title}} - Weierbot</title>

{{if .Prev}}Previous{{end}}
{{if .Next}}Next{{end}}

Weierbot

{{.Title}}

{{.Chatlog}}

`))

var reURL = regexp.MustCompile(`https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;]*[-A-Za-z0-9+&@#/%=~_|]`)
var reTime = regexp.MustCompile(`(?m)^\[\S+\]`)

type WeierBot struct {
	server     string
	client     *irc.Conn
	log        chan Message
	disconnect chan bool
}

func NewWeierBot(server, nick, password string, channels []string) *WeierBot {
	w := &WeierBot{
		server:     server,
		client:     irc.SimpleClient(nick),
		log:        make(chan Message, 512),
		disconnect: make(chan bool),
	}
	w.client.Me.Name = nick
	w.client.Me.Ident = nick

	w.client.AddHandler("connected", func(conn *irc.Conn, line *irc.Line) {
		conn.Privmsg("NickServ", "identify "+password)
		for _, ch := range channels {
			log.Printf("Joining %s\n", ch)
			conn.Join(ch)
		}
	})
	w.client.AddHandler("disconnected", func(conn *irc.Conn, line *irc.Line) {
		w.disconnect <- true
	})
	w.client.AddHandler("join", func(conn *irc.Conn, line *irc.Line) {
		if len(line.Args) >= 1 && strings.HasPrefix(line.Args[0], "#") {
			conn.Privmsg(line.Nick, fmt.Sprintf("Hallo %s, willkommen auf %s. "+
				"Dieser Channel wird unter http://weierbot.tux21b.org/ mitgespeichert.",
				line.Nick, line.Args[0]))
		}
	})
	w.client.AddHandler("privmsg", func(conn *irc.Conn, line *irc.Line) {
		if len(line.Args) < 2 {
			return
		}
		w.handleMessage(line.Args[0], Message{
			Nick:    line.Nick,
			Time:    line.Time,
			Message: line.Args[1],
		})
	})

	go w.writeLog()

	return w
}

func createMathbinImage(latexcode string) string {
	resp, err := http.Get(fmt.Sprintf(mathbinPreviewURL, url.QueryEscape("[EQ]"+latexcode+"[/EQ]")))
	if err != nil {
		return "error"
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	result := reMathbin.FindString(string(body))

	return fmt.Sprintf(mathbinURL, result)
}

func (bot *WeierBot) handleMessage(target string, msg Message) {
	if strings.HasPrefix(target, "#") {
		bot.log <- msg
	} else {
		target = msg.Nick
	}

	switch {
	case strings.HasPrefix(msg.Message, "!wolfram "):
		bot.send(target, fmt.Sprintf(wolframURL, url.QueryEscape(msg.Message[9:])))
	case msg.Message == "!log":
		bot.send(target, "http://weierbot.tux21b.org/")
	case strings.HasPrefix(msg.Message, "!mathbin "):
		imgurl := createMathbinImage(msg.Message[9:])
		bot.send(target, fmt.Sprintf(imgurl))
	}
}

func (bot *WeierBot) send(target, message string) {
	bot.client.Privmsg(target, message)
	if strings.HasPrefix(target, "#") {
		bot.log <- Message{Nick: bot.client.Me.Nick, Time: time.Now(),
			Message: message}
	}
}

func (bot *WeierBot) writeLog() {
	var (
		file     *os.File
		format   = "2006-01-02.log"
		filename string
	)
	defer func() {
		if file != nil {
			file.Close()
		}
	}()
	for msg := range bot.log {
		msg.Time = msg.Time.In(timezone)
		current := msg.Time.Format(format)
		if file == nil || current != filename {
			if file != nil {
				file.Close()
			}
			f, err := os.OpenFile(current,
				os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
			if err != nil {
				log.Println(err)
			} else {
				file, filename = f, current
			}
		}
		fmt.Fprintf(file, "%s\n", msg)
	}
}

func (bot *WeierBot) ServeIRC() {
	tries := 0
	for {
		tries++
		if err := bot.client.Connect(bot.server); err != nil {
			log.Println(err)
			if tries >= 5 {
				log.Fatal("maximum number of tries exceeded")
			}
			<-time.After(5 * time.Second)
			continue
		}
		tries = 0
		log.Printf("Connected to %s\n", bot.server)
		<-bot.disconnect
	}
}

func (bot *WeierBot) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	files, err := filepath.Glob("*.log")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	sort.Strings(files)

	if r.URL.Path == "/" && len(files) > 0 {
		http.Redirect(w, r, "/"+filepath.Base(files[len(files)-1]),
			http.StatusTemporaryRedirect)
	}
	index := sort.SearchStrings(files, r.URL.Path[1:])
	if index < 0 || index >= len(files) {
		http.NotFound(w, r)
		return
	}

	chatlog, err := ioutil.ReadFile(files[index])
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	chatlog = []byte(template.HTMLEscapeString(string(chatlog)))
	chatlog = reURL.ReplaceAllFunc(chatlog, func(m []byte) []byte {
		return []byte(fmt.Sprintf("%s", m, m))
	})
	chatlog = reTime.ReplaceAllFunc(chatlog, func(m []byte) []byte {
		return []byte(fmt.Sprintf("%s", m))
	})

	data := struct {
		Title      string
		Prev, Next string
		Chatlog    template.HTML
	}{
		Title:   filepath.Base(files[index]),
		Chatlog: template.HTML(chatlog),
	}
	if index > 0 {
		data.Prev = files[index-1]
	}
	if index < len(files)-1 {
		data.Next = files[index+1]
	}
	if err := tmpl.Execute(w, data); err != nil {
		log.Println(err.Error())
	}
}

type Message struct {
	Time    time.Time
	Nick    string
	Message string
}

func (m Message) String() string {
	return fmt.Sprintf("[%s] %s: %s", m.Time.Format(time.Kitchen), m.Nick, m.Message)
}

func main() {
	bot := NewWeierBot("irc.euirc.net", "weierbot2", "secret", []string{"#tm-test"})

	go func() {
		if err := http.ListenAndServe(":8005", bot); err != nil {
			log.Fatal(err)
		}
	}()

	bot.ServeIRC()
}
<|endoftext|>


<commit_before>package consensus

import (
	"errors"
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"

	"github.com/NebulousLabs/bolt"
)

var (
	errDoSBlock        = errors.New("block is known to be invalid")
	errNoBlockMap      = errors.New("block map is not in database")
	errInconsistentSet = errors.New("consensus set is not in a consistent state")
	errOrphan          = errors.New("block has no known parent")
)

// validateHeader does some early, low computation verification on the block.
// Callers should not assume that validation will happen in a particular order.
func (cs *ConsensusSet) validateHeader(tx dbTx, b types.Block) error {
	// See if the block is known already.
	id := b.ID()
	_, exists := cs.dosBlocks[id]
	if exists {
		return errDoSBlock
	}

	// Check if the block is already known.
	blockMap := tx.Bucket(BlockMap)
	if blockMap == nil {
		return errNoBlockMap
	}
	if blockMap.Get(id[:]) != nil {
		return modules.ErrBlockKnown
	}

	// Check for the parent.
	parentID := b.ParentID
	parentBytes := blockMap.Get(parentID[:])
	if parentBytes == nil {
		return errOrphan
	}

	var parent processedBlock
	err := cs.marshaler.Unmarshal(parentBytes, &parent)
	if err != nil {
		return err
	}
	// Check that the timestamp is not too far in the past to be acceptable.
	minTimestamp := cs.blockRuleHelper.minimumValidChildTimestamp(blockMap, &parent)

	return cs.blockValidator.ValidateBlock(b, minTimestamp, parent.ChildTarget, parent.Height+1)
}

// addBlockToTree inserts a block into the blockNode tree by adding it to its
// parent's list of children. If the new blockNode is heavier than the current
// node, the blockchain is forked to put the new block and its parents at the
// tip. An error will be returned if block verification fails or if the block
// does not extend the longest fork.
//
// addBlockToTree must use its own database update because it might need to
// modify the database while returning an error on the block. To prevent error
// tracking complexity, the error is handled inside the function so that 'nil'
// can be appropriately returned by the database and the transaction can be
// committed. Switching to a managed tx through bolt will make this complexity
// unneeded.
func (cs *ConsensusSet) addBlockToTree(b types.Block) (ce changeEntry, err error) {
	var nonExtending bool
	err = cs.db.Update(func(tx *bolt.Tx) error {
		pb, err := getBlockMap(tx, b.ParentID)
		if build.DEBUG && err != nil {
			panic(err)
		}
		currentNode := currentProcessedBlock(tx)
		newNode := cs.newChild(tx, pb, b)

		// modules.ErrNonExtendingBlock should be returned if the block does
		// not extend the current blockchain, however the changes from newChild
		// should be comitted (which means 'nil' must be returned). A flag is
		// set to indicate that modules.ErrNonExtending should be returned.
		nonExtending = !newNode.heavierThan(currentNode)
		if nonExtending {
			return nil
		}
		var revertedBlocks, appliedBlocks []*processedBlock
		revertedBlocks, appliedBlocks, err = cs.forkBlockchain(tx, newNode)
		if err != nil {
			return err
		}
		for _, rn := range revertedBlocks {
			ce.RevertedBlocks = append(ce.RevertedBlocks, rn.Block.ID())
		}
		for _, an := range appliedBlocks {
			ce.AppliedBlocks = append(ce.AppliedBlocks, an.Block.ID())
		}
		// To have correct error handling, appendChangeLog must be called
		// before appending to the in-memory changelog. If this call fails, the
		// change is going to be reverted, but the in-memory changelog is not
		// going to be reverted.
		//
		// Technically, if bolt fails for some other reason (such as a
		// filesystem error), the in-memory changelog will be incorrect anyway.
		// Restarting Sia will fix it. The in-memory changelog is being phased
		// out.
		err = appendChangeLog(tx, ce)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return changeEntry{}, err
	}
	if nonExtending {
		return changeEntry{}, modules.ErrNonExtendingBlock
	}
	return ce, nil
}

// managedAcceptBlock accepts a block but does not broadcast it to any peers.
// See comment for AcceptBlock. Typically AcceptBlock should be used. This
// method should only be used when there would otherwise be multiple
// consecutive calls to AcceptBlock with each successive call accepting the
// child block of the previous call.
func (cs *ConsensusSet) managedAcceptBlock(b types.Block) error {
	// Grab a lock on the consensus set. Lock is demoted later in the function,
	// failure to unlock before returning an error will cause a deadlock.
	cs.mu.Lock()

	// Start verification inside of a bolt View tx.
	err := cs.db.View(func(tx *bolt.Tx) error {
		// Do not accept a block if the database is inconsistent.
		if inconsistencyDetected(tx) {
			return errors.New("inconsistent database")
		}

		// Check that the header is valid. The header is checked first because it
		// is not computationally expensive to verify, but it is computationally
		// expensive to create.
		err := cs.validateHeader(boltTxWrapper{tx}, b)
		if err != nil {
			// If the block is in the near future, but too far to be acceptable, then
			// save the block and add it to the consensus set after it is no longer
			// too far in the future.
			if err == errFutureTimestamp {
				go func() {
					time.Sleep(time.Duration(b.Timestamp-(types.CurrentTimestamp()+types.FutureThreshold)) * time.Second)
					cs.AcceptBlock(b) // NOTE: Error is not handled.
				}()
			}
			return err
		}
		return nil
	})
	if err != nil {
		cs.mu.Unlock()
		return err
	}

	// Try adding the block to the block tree. This call will perform
	// verification on the block before adding the block to the block tree. An
	// error is returned if verification fails or if the block does not extend
	// the longest fork.
	changeEntry, err := cs.addBlockToTree(b)
	if err != nil {
		cs.mu.Unlock()
		return err
	}
	// If appliedBlocks is 0, revertedBlocks will also be 0.
	if build.DEBUG && len(changeEntry.AppliedBlocks) == 0 && len(changeEntry.RevertedBlocks) != 0 {
		panic("appliedBlocks and revertedBlocks are mismatched!")
	}

	// Updates complete, demote the lock.
	cs.mu.Demote()
	defer cs.mu.DemotedUnlock()
	if len(changeEntry.AppliedBlocks) > 0 {
		cs.readlockUpdateSubscribers(changeEntry)
	}
	return nil
}

// AcceptBlock will add a block to the state, forking the blockchain if it is
// on a fork that is heavier than the current fork. If the block is accepted,
// it will be relayed to connected peers. This function should only be called
// for new, untrusted blocks.
func (cs *ConsensusSet) AcceptBlock(b types.Block) error {
	err := cs.managedAcceptBlock(b)
	if err != nil {
		return err
	}
	// Broadcast the new block to all peers.
	go cs.gateway.Broadcast("RelayBlock", b)
	return nil
}
<commit_msg>Improve comments for (managed)AcceptBlock<commit_after>package consensus

import (
	"errors"
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"

	"github.com/NebulousLabs/bolt"
)

var (
	errDoSBlock        = errors.New("block is known to be invalid")
	errNoBlockMap      = errors.New("block map is not in database")
	errInconsistentSet = errors.New("consensus set is not in a consistent state")
	errOrphan          = errors.New("block has no known parent")
)

// validateHeader does some early, low computation verification on the block.
// Callers should not assume that validation will happen in a particular order.
func (cs *ConsensusSet) validateHeader(tx dbTx, b types.Block) error {
	// See if the block is known already.
	id := b.ID()
	_, exists := cs.dosBlocks[id]
	if exists {
		return errDoSBlock
	}

	// Check if the block is already known.
	blockMap := tx.Bucket(BlockMap)
	if blockMap == nil {
		return errNoBlockMap
	}
	if blockMap.Get(id[:]) != nil {
		return modules.ErrBlockKnown
	}

	// Check for the parent.
	parentID := b.ParentID
	parentBytes := blockMap.Get(parentID[:])
	if parentBytes == nil {
		return errOrphan
	}

	var parent processedBlock
	err := cs.marshaler.Unmarshal(parentBytes, &parent)
	if err != nil {
		return err
	}
	// Check that the timestamp is not too far in the past to be acceptable.
	minTimestamp := cs.blockRuleHelper.minimumValidChildTimestamp(blockMap, &parent)

	return cs.blockValidator.ValidateBlock(b, minTimestamp, parent.ChildTarget, parent.Height+1)
}

// addBlockToTree inserts a block into the blockNode tree by adding it to its
// parent's list of children. If the new blockNode is heavier than the current
// node, the blockchain is forked to put the new block and its parents at the
// tip. An error will be returned if block verification fails or if the block
// does not extend the longest fork.
//
// addBlockToTree must use its own database update because it might need to
// modify the database while returning an error on the block. To prevent error
// tracking complexity, the error is handled inside the function so that 'nil'
// can be appropriately returned by the database and the transaction can be
// committed. Switching to a managed tx through bolt will make this complexity
// unneeded.
func (cs *ConsensusSet) addBlockToTree(b types.Block) (ce changeEntry, err error) {
	var nonExtending bool
	err = cs.db.Update(func(tx *bolt.Tx) error {
		pb, err := getBlockMap(tx, b.ParentID)
		if build.DEBUG && err != nil {
			panic(err)
		}
		currentNode := currentProcessedBlock(tx)
		newNode := cs.newChild(tx, pb, b)

		// modules.ErrNonExtendingBlock should be returned if the block does
		// not extend the current blockchain, however the changes from newChild
		// should be comitted (which means 'nil' must be returned). A flag is
		// set to indicate that modules.ErrNonExtending should be returned.
		nonExtending = !newNode.heavierThan(currentNode)
		if nonExtending {
			return nil
		}
		var revertedBlocks, appliedBlocks []*processedBlock
		revertedBlocks, appliedBlocks, err = cs.forkBlockchain(tx, newNode)
		if err != nil {
			return err
		}
		for _, rn := range revertedBlocks {
			ce.RevertedBlocks = append(ce.RevertedBlocks, rn.Block.ID())
		}
		for _, an := range appliedBlocks {
			ce.AppliedBlocks = append(ce.AppliedBlocks, an.Block.ID())
		}
		// To have correct error handling, appendChangeLog must be called
		// before appending to the in-memory changelog. If this call fails, the
		// change is going to be reverted, but the in-memory changelog is not
		// going to be reverted.
		//
		// Technically, if bolt fails for some other reason (such as a
		// filesystem error), the in-memory changelog will be incorrect anyway.
		// Restarting Sia will fix it. The in-memory changelog is being phased
		// out.
		err = appendChangeLog(tx, ce)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return changeEntry{}, err
	}
	if nonExtending {
		return changeEntry{}, modules.ErrNonExtendingBlock
	}
	return ce, nil
}

// managedAcceptBlock will try to add a block to the consensus set. If the
// block does not extend the longest currently known chain, an error is
// returned but the block is still kept in memory. If the block extends a fork
// such that the fork becomes the longest currently known chain, the consensus
// set will reorganize itself to recognize the new longest fork. Accepted
// blocks are not relayed.
//
// Typically AcceptBlock should be used so that the accepted block is relayed.
// This method is typically only be used when there would otherwise be multiple
// consecutive calls to AcceptBlock with each successive call accepting the
// child block of the previous call.
func (cs *ConsensusSet) managedAcceptBlock(b types.Block) error {
	// Grab a lock on the consensus set. Lock is demoted later in the function,
	// failure to unlock before returning an error will cause a deadlock.
	cs.mu.Lock()

	// Start verification inside of a bolt View tx.
	err := cs.db.View(func(tx *bolt.Tx) error {
		// Do not accept a block if the database is inconsistent.
		if inconsistencyDetected(tx) {
			return errors.New("inconsistent database")
		}

		// Check that the header is valid. The header is checked first because it
		// is not computationally expensive to verify, but it is computationally
		// expensive to create.
		err := cs.validateHeader(boltTxWrapper{tx}, b)
		if err != nil {
			// If the block is in the near future, but too far to be acceptable, then
			// save the block and add it to the consensus set after it is no longer
			// too far in the future.
			if err == errFutureTimestamp {
				go func() {
					time.Sleep(time.Duration(b.Timestamp-(types.CurrentTimestamp()+types.FutureThreshold)) * time.Second)
					cs.AcceptBlock(b) // NOTE: Error is not handled.
				}()
			}
			return err
		}
		return nil
	})
	if err != nil {
		cs.mu.Unlock()
		return err
	}

	// Try adding the block to the block tree. This call will perform
	// verification on the block before adding the block to the block tree. An
	// error is returned if verification fails or if the block does not extend
	// the longest fork.
	changeEntry, err := cs.addBlockToTree(b)
	if err != nil {
		cs.mu.Unlock()
		return err
	}
	// If appliedBlocks is 0, revertedBlocks will also be 0.
	if build.DEBUG && len(changeEntry.AppliedBlocks) == 0 && len(changeEntry.RevertedBlocks) != 0 {
		panic("appliedBlocks and revertedBlocks are mismatched!")
	}

	// Updates complete, demote the lock.
	cs.mu.Demote()
	defer cs.mu.DemotedUnlock()
	if len(changeEntry.AppliedBlocks) > 0 {
		cs.readlockUpdateSubscribers(changeEntry)
	}
	return nil
}

// AcceptBlock will try to add a block to the consensus set. If the block does
// not extend the longest currently known chain, an error is returned but the
// block is still kept in memory. If the block extends a fork such that the
// fork becomes the longest currently known chain, the consensus set will
// reorganize itself to recognize the new longest fork. If a block is accepted
// without error, it will be relayed to all connected peers. This function
// should only be called for new blocks.
func (cs *ConsensusSet) AcceptBlock(b types.Block) error {
	err := cs.managedAcceptBlock(b)
	if err != nil {
		return err
	}
	// Broadcast the new block to all peers.
	go cs.gateway.Broadcast("RelayBlock", b)
	return nil
}
<|endoftext|>


<commit_before>package checkList

import (
	"log"
	"os/exec"
	"sync"

	"github.com/karolgorecki/goprove/util"
)

var wg sync.WaitGroup

// checkItem is the basic sturct for the test cases
type checkItem struct {
	name     string
	function func(string) (message string, success bool)
}

func get() (checkList map[string]checkItem) {

	checkList = map[string]checkItem{
		"builds": {
			name:     "Compiles: Does the project build?",
			function: builds,
		},
		"isFormated": {
			name:     "gofmt Correctness: Is the code formatted correctly?",
			function: isFormatted,
		},
		"hasLicense": {
			name:     "Licensed: Does the project have a license?",
			function: hasLicense,
		},
		"isLinted": {
			name:     "golint Correctness: Is the linter satisfied?",
			function: isLinted,
		},
		"isVet": {
			name:     "go tool vet Correctness: Is the Go vet satisfied?",
			function: isVet,
		},
		"hasReadme": {
			name:     "README Presence: Does the project's include a documentation entrypoint?",
			function: hasReadme,
		},
		"hasContribution": {
			name:     "Contribution Process: Does the project document a contribution process?",
			function: hasContribution,
		},
	}

	return checkList
}

func (checkItem checkItem) Run() (message string, success bool) {
	return checkItem.function(checkItem.name)
}

// RunTasks is a wrapper for running all tasks from the list
func RunTasks() (successTasks []string, failedTasks []string) {
	tasks := get()

	wg.Add(len(tasks))
	for _, task := range tasks {

		go func(task checkItem) {
			desc, isSuccess := task.Run()

			if isSuccess {
				successTasks = append(successTasks, desc)

			} else {
				failedTasks = append(failedTasks, desc)
			}

			wg.Done()

		}(task)
	}

	wg.Wait()

	return successTasks, failedTasks
}

// -----------------------------------------------------------------------------
// CHECKLIST FUNCTIONS
// -----------------------------------------------------------------------------
func builds(taskName string) (message string, success bool) {
	_, err := exec.Command("go", "build").Output()

	if err != nil {
		return util.GetFailMessage(taskName), false
	}
	return util.GetSuccessMessage(taskName), true
}

func isFormatted(taskName string) (message string, success bool) {
	output, _ := exec.Command("gofmt", "-l", ".").Output()

	if len(output) > 0 {
		return util.GetFailMessage(taskName), false
	}

	return util.GetSuccessMessage(taskName), true
}

func hasLicense(taskName string) (message string, success bool) {
	hasLicense, err := util.FileExists("license", "licensing")

	if err != nil {
		log.Fatal(err)
	}

	if hasLicense {
		return util.GetSuccessMessage(taskName), true
	}

	return util.GetFailMessage(taskName), false
}

func hasReadme(taskName string) (message string, success bool) {
	hasReadme, err := util.FileExists("readme")
	if err != nil {
		log.Fatal(err)
	}

	if hasReadme {
		return util.GetSuccessMessage(taskName), true
	}

	return util.GetFailMessage(taskName), false
}

func hasContribution(taskName string) (message string, success bool) {
	hasContribution, err := util.FileExists("contribution", "contribute", "contributing")
	if err != nil {
		log.Fatal(err)
	}

	if hasContribution {
		return util.GetSuccessMessage(taskName), true
	}

	return util.GetFailMessage(taskName), false
}

func isLinted(taskName string) (string, bool) {

	output, _ := exec.Command("golint").Output()

	if len(output) > 0 {
		return util.GetFailMessage(taskName), false
	}
	return util.GetSuccessMessage(taskName), true
}

func isVet(taskName string) (message string, success bool) {
	_, err := exec.Command("go", "vet").Output()

	if err != nil {
		return util.GetFailMessage(taskName), false
	}
	return util.GetSuccessMessage(taskName), true
}
<commit_msg>feat(checkList): Added test that checks if test are passing<commit_after>package checkList

import (
	"log"
	"os/exec"
	"regexp"
	"sync"

	"github.com/karolgorecki/goprove/util"
)

var wg sync.WaitGroup

// checkItem is the basic sturct for the test cases
type checkItem struct {
	name     string
	function func(string) (message string, success bool)
}

func get() (checkList map[string]checkItem) {

	checkList = map[string]checkItem{
		"builds": {
			name:     "Compiles: Does the project build?",
			function: builds,
		},
		"isFormated": {
			name:     "gofmt Correctness: Is the code formatted correctly?",
			function: isFormatted,
		},
		"hasLicense": {
			name:     "Licensed: Does the project have a license?",
			function: hasLicense,
		},
		"isLinted": {
			name:     "golint Correctness: Is the linter satisfied?",
			function: isLinted,
		},
		"isVet": {
			name:     "go tool vet Correctness: Is the Go vet satisfied?",
			function: isVet,
		},
		"hasReadme": {
			name:     "README Presence: Does the project's include a documentation entrypoint?",
			function: hasReadme,
		},
		"hasContribution": {
			name:     "Contribution Process: Does the project document a contribution process?",
			function: hasContribution,
		},
		"testPassing": {
			name:     "Are the tests passing?",
			function: testPassing,
		},
	}

	return checkList
}

func (checkItem checkItem) Run() (message string, success bool) {
	return checkItem.function(checkItem.name)
}

// RunTasks is a wrapper for running all tasks from the list
func RunTasks() (successTasks []string, failedTasks []string) {
	tasks := get()

	wg.Add(len(tasks))
	for _, task := range tasks {

		go func(task checkItem) {
			desc, isSuccess := task.Run()

			if isSuccess {
				successTasks = append(successTasks, desc)

			} else {
				failedTasks = append(failedTasks, desc)
			}

			wg.Done()

		}(task)
	}

	wg.Wait()

	return successTasks, failedTasks
}

// -----------------------------------------------------------------------------
// CHECKLIST FUNCTIONS
// -----------------------------------------------------------------------------
func builds(taskName string) (message string, success bool) {
	_, err := exec.Command("go", "build").Output()

	if err != nil {
		return util.GetFailMessage(taskName), false
	}
	return util.GetSuccessMessage(taskName), true
}

func isFormatted(taskName string) (message string, success bool) {
	output, _ := exec.Command("gofmt", "-l", ".").Output()

	if len(output) > 0 {
		return util.GetFailMessage(taskName), false
	}

	return util.GetSuccessMessage(taskName), true
}

func testPassing(taskName string) (message string, success bool) {
	output, _ := exec.Command("go", "test", "./...").Output()

	if testFails, _ := regexp.Match(`--- FAIL`, output); testFails {
		return util.GetFailMessage(taskName), false
	}

	return util.GetSuccessMessage(taskName), true
}

func hasLicense(taskName string) (message string, success bool) {
	hasLicense, err := util.FileExists("license", "licensing")

	if err != nil {
		log.Fatal(err)
	}

	if hasLicense {
		return util.GetSuccessMessage(taskName), true
	}

	return util.GetFailMessage(taskName), false
}

func hasReadme(taskName string) (message string, success bool) {
	hasReadme, err := util.FileExists("readme")
	if err != nil {
		log.Fatal(err)
	}

	if hasReadme {
		return util.GetSuccessMessage(taskName), true
	}

	return util.GetFailMessage(taskName), false
}

func hasContribution(taskName string) (message string, success bool) {
	hasContribution, err := util.FileExists("contribution", "contribute", "contributing")
	if err != nil {
		log.Fatal(err)
	}

	if hasContribution {
		return util.GetSuccessMessage(taskName), true
	}

	return util.GetFailMessage(taskName), false
}

func isLinted(taskName string) (string, bool) {

	output, _ := exec.Command("golint").Output()

	if len(output) > 0 {
		return util.GetFailMessage(taskName), false
	}
	return util.GetSuccessMessage(taskName), true
}

func isVet(taskName string) (message string, success bool) {
	_, err := exec.Command("go", "vet").Output()

	if err != nil {
		return util.GetFailMessage(taskName), false
	}
	return util.GetSuccessMessage(taskName), true
}
<|endoftext|>


<commit_before>package middleware

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"regexp"
)

// Dynamic routing based on host given by a map.
func VHOST(plan Plan) func(*gin.Context) {
	portmatch := regexp.MustCompile(":.*$")
	return func(c *gin.Context) {
		host := c.Request.Host
		hostwithoutport := portmatch.ReplaceAllLiteralString(host, "")
		fmt.Println(hostwithoutport)

		if plan[host] != nil {
			fmt.Println("Found")
			plan[host](c)
		} else if plan[hostwithoutport] != nil {
			fmt.Println("Found without port")
			plan[hostwithoutport](c)
		} else if plan["***"] != nil {
			fmt.Println("Found catchall")
			plan["***"](c)
		} else {
			fmt.Println("Found nothing")
			c.Next()
		}
	}
}
<commit_msg>I have no idea what happened, but everything just stopped working.<commit_after>package middleware

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"regexp"
)

// Dynamic routing based on host given by a map.
func VHOST(plan Plan) func(*gin.Context) {
	portmatch := regexp.MustCompile(":.*$")
	return func(c *gin.Context) {
		host := c.Request.Host
		hostwithoutport := portmatch.ReplaceAllLiteralString(host, "")
		fmt.Println(hostwithoutport)

		if plan[host] != nil {
			fmt.Println("Found")
			plan[host](c)
		} else if plan[hostwithoutport] != nil {
			fmt.Println("Found without port")
			plan[hostwithoutport](c)
		} else if plan["***"] != nil {
			fmt.Println("Found catchall")
			plan["***"](c)
		} else {
			fmt.Println("Found nothing")
			c.Data(404, "text/plain", []byte("404 page not found"))
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.

package watch

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

	"github.com/ActiveState/tail/util"
	"gopkg.in/fsnotify.v0"
	"gopkg.in/tomb.v1"
)

var inotifyTracker *InotifyTracker

// InotifyFileWatcher uses inotify to monitor file changes.
type InotifyFileWatcher struct {
	Filename string
	Size     int64
	w        *fsnotify.Watcher
}

func NewInotifyFileWatcher(filename string, w *fsnotify.Watcher) *InotifyFileWatcher {
	fw := &InotifyFileWatcher{filename, 0, w}
	return fw
}

func (fw *InotifyFileWatcher) BlockUntilExists(t *tomb.Tomb) error {
	dirname := filepath.Dir(fw.Filename)

	// Watch for new files to be created in the parent directory.
	err := fw.w.WatchFlags(dirname, fsnotify.FSN_CREATE)
	if err != nil {
		return err
	}
	defer fw.w.RemoveWatch(dirname)

	// Do a real check now as the file might have been created before
	// calling `WatchFlags` above.
	if _, err = os.Stat(fw.Filename); !os.IsNotExist(err) {
		// file exists, or stat returned an error.
		return err
	}

	for {
		select {
		case evt, ok := <-fw.w.Event:
			if !ok {
				return fmt.Errorf("inotify watcher has been closed")
			} else if evt.Name == fw.Filename {
				return nil
			}
		case <-t.Dying():
			return tomb.ErrDying
		}
	}
	panic("unreachable")
}

func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, fi os.FileInfo) *FileChanges {
	changes := NewFileChanges()

	err := fw.w.Watch(fw.Filename)
	if err != nil {
		util.Fatal("Error watching %v: %v", fw.Filename, err)
	}

	fw.Size = fi.Size()

	go func() {
		defer fw.w.RemoveWatch(fw.Filename)
		defer changes.Close()

		for {
			prevSize := fw.Size

			var evt *fsnotify.FileEvent
			var ok bool

			select {
			case evt, ok = <-fw.w.Event:
				if !ok {
					return
				}
			case <-t.Dying():
				return
			}

			switch {
			case evt.IsDelete():
				fallthrough

			case evt.IsRename():
				changes.NotifyDeleted()
				return

			case evt.IsModify():
				fi, err := os.Stat(fw.Filename)
				if err != nil {
					if os.IsNotExist(err) {
						changes.NotifyDeleted()
						return
					}
					// XXX: report this error back to the user
					util.Fatal("Failed to stat file %v: %v", fw.Filename, err)
				}
				fw.Size = fi.Size()

				if prevSize > 0 && prevSize > fw.Size {
					changes.NotifyTruncated()
				} else {
					changes.NotifyModified()
				}
				prevSize = fw.Size
			}
		}
	}()

	return changes
}
<commit_msg>minor<commit_after>// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.

package watch

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

	"github.com/ActiveState/tail/util"
	"gopkg.in/fsnotify.v0"
	"gopkg.in/tomb.v1"
)

// InotifyFileWatcher uses inotify to monitor file changes.
type InotifyFileWatcher struct {
	Filename string
	Size     int64
	w        *fsnotify.Watcher
}

func NewInotifyFileWatcher(filename string, w *fsnotify.Watcher) *InotifyFileWatcher {
	fw := &InotifyFileWatcher{filename, 0, w}
	return fw
}

func (fw *InotifyFileWatcher) BlockUntilExists(t *tomb.Tomb) error {
	dirname := filepath.Dir(fw.Filename)

	// Watch for new files to be created in the parent directory.
	err := fw.w.WatchFlags(dirname, fsnotify.FSN_CREATE)
	if err != nil {
		return err
	}
	defer fw.w.RemoveWatch(dirname)

	// Do a real check now as the file might have been created before
	// calling `WatchFlags` above.
	if _, err = os.Stat(fw.Filename); !os.IsNotExist(err) {
		// file exists, or stat returned an error.
		return err
	}

	for {
		select {
		case evt, ok := <-fw.w.Event:
			if !ok {
				return fmt.Errorf("inotify watcher has been closed")
			} else if evt.Name == fw.Filename {
				return nil
			}
		case <-t.Dying():
			return tomb.ErrDying
		}
	}
	panic("unreachable")
}

func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, fi os.FileInfo) *FileChanges {
	changes := NewFileChanges()

	err := fw.w.Watch(fw.Filename)
	if err != nil {
		util.Fatal("Error watching %v: %v", fw.Filename, err)
	}

	fw.Size = fi.Size()

	go func() {
		defer fw.w.RemoveWatch(fw.Filename)
		defer changes.Close()

		for {
			prevSize := fw.Size

			var evt *fsnotify.FileEvent
			var ok bool

			select {
			case evt, ok = <-fw.w.Event:
				if !ok {
					return
				}
			case <-t.Dying():
				return
			}

			switch {
			case evt.IsDelete():
				fallthrough

			case evt.IsRename():
				changes.NotifyDeleted()
				return

			case evt.IsModify():
				fi, err := os.Stat(fw.Filename)
				if err != nil {
					if os.IsNotExist(err) {
						changes.NotifyDeleted()
						return
					}
					// XXX: report this error back to the user
					util.Fatal("Failed to stat file %v: %v", fw.Filename, err)
				}
				fw.Size = fi.Size()

				if prevSize > 0 && prevSize > fw.Size {
					changes.NotifyTruncated()
				} else {
					changes.NotifyModified()
				}
				prevSize = fw.Size
			}
		}
	}()

	return changes
}
<|endoftext|>


<commit_before>// Copyright (c) Alex Ellis 2017. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/openfaas/faas/watchdog/types"
)

func buildFunctionInput(config *WatchdogConfig, r *http.Request) ([]byte, error) {
	var res []byte
	var requestBytes []byte
	var err error

	if r.Body != nil {
		defer r.Body.Close()
	}

	requestBytes, err = ioutil.ReadAll(r.Body)
	if config.marshalRequest {
		marshalRes, marshalErr := types.MarshalRequest(requestBytes, &r.Header)
		err = marshalErr
		res = marshalRes
	} else {
		res = requestBytes
	}
	return res, err
}

func debugHeaders(source *http.Header, direction string) {
	for k, vv := range *source {
		fmt.Printf("[%s] %s=%s\n", direction, k, vv)
	}
}

type requestInfo struct {
	headerWritten bool
}

func pipeRequest(config *WatchdogConfig, w http.ResponseWriter, r *http.Request, method string, hasBody bool) {
	startTime := time.Now()

	parts := strings.Split(config.faasProcess, " ")

	ri := &requestInfo{}

	if config.debugHeaders {
		debugHeaders(&r.Header, "in")
	}

	log.Println("Forking fprocess.")

	targetCmd := exec.Command(parts[0], parts[1:]...)

	envs := getAdditionalEnvs(config, r, method)
	if len(envs) > 0 {
		targetCmd.Env = envs
	}

	writer, _ := targetCmd.StdinPipe()

	var out []byte
	var err error
	var requestBody []byte

	var wg sync.WaitGroup

	wgCount := 2
	if hasBody == false {
		wgCount = 1
	}

	if hasBody {
		var buildInputErr error
		requestBody, buildInputErr = buildFunctionInput(config, r)
		if buildInputErr != nil {
			ri.headerWritten = true
			w.WriteHeader(http.StatusBadRequest)
			// I.e. "exit code 1"
			w.Write([]byte(buildInputErr.Error()))

			// Verbose message - i.e. stack trace
			w.Write([]byte("\n"))
			w.Write(out)

			return
		}
	}

	wg.Add(wgCount)

	var timer *time.Timer

	if config.execTimeout > 0*time.Second {
		timer = time.NewTimer(config.execTimeout)

		go func() {
			<-timer.C
			log.Printf("Killing process: %s\n", config.faasProcess)
			if targetCmd != nil && targetCmd.Process != nil {
				ri.headerWritten = true
				w.WriteHeader(http.StatusRequestTimeout)

				w.Write([]byte("Killed process.\n"))

				val := targetCmd.Process.Kill()
				if val != nil {
					log.Printf("Killed process: %s - error %s\n", config.faasProcess, val.Error())
				}
			}
		}()
	}

	// Only write body if this is appropriate for the method.
	if hasBody {
		// Write to pipe in separate go-routine to prevent blocking
		go func() {
			defer wg.Done()
			writer.Write(requestBody)
			writer.Close()
		}()
	}

	go func() {
		defer wg.Done()
		out, err = targetCmd.CombinedOutput()
	}()

	wg.Wait()
	if timer != nil {
		timer.Stop()
	}

	if err != nil {
		if config.writeDebug == true {
			log.Printf("Success=%t, Error=%s\n", targetCmd.ProcessState.Success(), err.Error())
			log.Printf("Out=%s\n", out)
		}

		if ri.headerWritten == false {
			w.WriteHeader(http.StatusInternalServerError)
			response := bytes.NewBufferString(err.Error())
			w.Write(response.Bytes())
			w.Write([]byte("\n"))
			if len(out) > 0 {
				w.Write(out)
			}
			ri.headerWritten = true
		}
		return
	}

	var bytesWritten string
	if config.writeDebug == true {
		os.Stdout.Write(out)
	} else {
		bytesWritten = fmt.Sprintf("Wrote %d Bytes", len(out))
	}

	if len(config.contentType) > 0 {
		w.Header().Set("Content-Type", config.contentType)
	} else {

		// Match content-type of caller if no override specified.
		clientContentType := r.Header.Get("Content-Type")
		if len(clientContentType) > 0 {
			w.Header().Set("Content-Type", clientContentType)
		}
	}

	execTime := time.Since(startTime).Seconds()
	if ri.headerWritten == false {
		w.Header().Set("X-Duration-Seconds", fmt.Sprintf("%f", execTime))
		ri.headerWritten = true
		w.WriteHeader(200)
		w.Write(out)
	}

	if config.debugHeaders {
		header := w.Header()
		debugHeaders(&header, "out")
	}
	if len(bytesWritten) > 0 {
		log.Printf("%s - Duration: %f seconds", bytesWritten, execTime)
	} else {
		log.Printf("Duration: %f seconds", execTime)
	}
}

func getAdditionalEnvs(config *WatchdogConfig, r *http.Request, method string) []string {
	var envs []string

	if config.cgiHeaders {
		envs = os.Environ()
		for k, v := range r.Header {
			kv := fmt.Sprintf("Http_%s=%s", strings.Replace(k, "-", "_", -1), v[0])
			envs = append(envs, kv)
		}

		envs = append(envs, fmt.Sprintf("Http_Method=%s", method))
		envs = append(envs, fmt.Sprintf("Http_ContentLength=%d", r.ContentLength))

		if config.writeDebug {
			log.Println("Query ", r.URL.RawQuery)
		}
		if len(r.URL.RawQuery) > 0 {
			envs = append(envs, fmt.Sprintf("Http_Query=%s", r.URL.RawQuery))
		}

		if config.writeDebug {
			log.Println("Path ", r.URL.Path)
		}
		if len(r.URL.Path) > 0 {
			envs = append(envs, fmt.Sprintf("Http_Path=%s", r.URL.Path))
		}

	}

	return envs
}

func makeRequestHandler(config *WatchdogConfig) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case
			"POST",
			"PUT",
			"DELETE",
			"UPDATE":
			pipeRequest(config, w, r, r.Method, true)
			break
		case
			"GET":
			pipeRequest(config, w, r, r.Method, false)
			break
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)

		}
	}
}

func main() {
	osEnv := types.OsEnv{}
	readConfig := ReadConfig{}
	config := readConfig.Read(osEnv)

	if len(config.faasProcess) == 0 {
		log.Panicln("Provide a valid process via fprocess environmental variable.")
		return
	}

	readTimeout := config.readTimeout
	writeTimeout := config.writeTimeout

	s := &http.Server{
		Addr:           ":8080",
		ReadTimeout:    readTimeout,
		WriteTimeout:   writeTimeout,
		MaxHeaderBytes: 1 << 20, // Max header of 1MB
	}

	http.HandleFunc("/", makeRequestHandler(&config))

	path := filepath.Join(os.TempDir(), ".lock")
	log.Printf("Writing lock-file to: %s\n", path)
	writeErr := ioutil.WriteFile(path, []byte{}, 0660)
	if writeErr != nil {
		log.Panicf("Cannot write %s. To disable lock-file set env suppress_lock=true.\n Error: %s.\n", path, writeErr.Error())
	}
	log.Fatal(s.ListenAndServe())
}
<commit_msg>Re-enable the suppress-lock feature<commit_after>// Copyright (c) Alex Ellis 2017. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/openfaas/faas/watchdog/types"
)

func buildFunctionInput(config *WatchdogConfig, r *http.Request) ([]byte, error) {
	var res []byte
	var requestBytes []byte
	var err error

	if r.Body != nil {
		defer r.Body.Close()
	}

	requestBytes, err = ioutil.ReadAll(r.Body)
	if config.marshalRequest {
		marshalRes, marshalErr := types.MarshalRequest(requestBytes, &r.Header)
		err = marshalErr
		res = marshalRes
	} else {
		res = requestBytes
	}
	return res, err
}

func debugHeaders(source *http.Header, direction string) {
	for k, vv := range *source {
		fmt.Printf("[%s] %s=%s\n", direction, k, vv)
	}
}

type requestInfo struct {
	headerWritten bool
}

func pipeRequest(config *WatchdogConfig, w http.ResponseWriter, r *http.Request, method string, hasBody bool) {
	startTime := time.Now()

	parts := strings.Split(config.faasProcess, " ")

	ri := &requestInfo{}

	if config.debugHeaders {
		debugHeaders(&r.Header, "in")
	}

	log.Println("Forking fprocess.")

	targetCmd := exec.Command(parts[0], parts[1:]...)

	envs := getAdditionalEnvs(config, r, method)
	if len(envs) > 0 {
		targetCmd.Env = envs
	}

	writer, _ := targetCmd.StdinPipe()

	var out []byte
	var err error
	var requestBody []byte

	var wg sync.WaitGroup

	wgCount := 2
	if hasBody == false {
		wgCount = 1
	}

	if hasBody {
		var buildInputErr error
		requestBody, buildInputErr = buildFunctionInput(config, r)
		if buildInputErr != nil {
			ri.headerWritten = true
			w.WriteHeader(http.StatusBadRequest)
			// I.e. "exit code 1"
			w.Write([]byte(buildInputErr.Error()))

			// Verbose message - i.e. stack trace
			w.Write([]byte("\n"))
			w.Write(out)

			return
		}
	}

	wg.Add(wgCount)

	var timer *time.Timer

	if config.execTimeout > 0*time.Second {
		timer = time.NewTimer(config.execTimeout)

		go func() {
			<-timer.C
			log.Printf("Killing process: %s\n", config.faasProcess)
			if targetCmd != nil && targetCmd.Process != nil {
				ri.headerWritten = true
				w.WriteHeader(http.StatusRequestTimeout)

				w.Write([]byte("Killed process.\n"))

				val := targetCmd.Process.Kill()
				if val != nil {
					log.Printf("Killed process: %s - error %s\n", config.faasProcess, val.Error())
				}
			}
		}()
	}

	// Only write body if this is appropriate for the method.
	if hasBody {
		// Write to pipe in separate go-routine to prevent blocking
		go func() {
			defer wg.Done()
			writer.Write(requestBody)
			writer.Close()
		}()
	}

	go func() {
		defer wg.Done()
		out, err = targetCmd.CombinedOutput()
	}()

	wg.Wait()
	if timer != nil {
		timer.Stop()
	}

	if err != nil {
		if config.writeDebug == true {
			log.Printf("Success=%t, Error=%s\n", targetCmd.ProcessState.Success(), err.Error())
			log.Printf("Out=%s\n", out)
		}

		if ri.headerWritten == false {
			w.WriteHeader(http.StatusInternalServerError)
			response := bytes.NewBufferString(err.Error())
			w.Write(response.Bytes())
			w.Write([]byte("\n"))
			if len(out) > 0 {
				w.Write(out)
			}
			ri.headerWritten = true
		}
		return
	}

	var bytesWritten string
	if config.writeDebug == true {
		os.Stdout.Write(out)
	} else {
		bytesWritten = fmt.Sprintf("Wrote %d Bytes", len(out))
	}

	if len(config.contentType) > 0 {
		w.Header().Set("Content-Type", config.contentType)
	} else {

		// Match content-type of caller if no override specified.
		clientContentType := r.Header.Get("Content-Type")
		if len(clientContentType) > 0 {
			w.Header().Set("Content-Type", clientContentType)
		}
	}

	execTime := time.Since(startTime).Seconds()
	if ri.headerWritten == false {
		w.Header().Set("X-Duration-Seconds", fmt.Sprintf("%f", execTime))
		ri.headerWritten = true
		w.WriteHeader(200)
		w.Write(out)
	}

	if config.debugHeaders {
		header := w.Header()
		debugHeaders(&header, "out")
	}
	if len(bytesWritten) > 0 {
		log.Printf("%s - Duration: %f seconds", bytesWritten, execTime)
	} else {
		log.Printf("Duration: %f seconds", execTime)
	}
}

func getAdditionalEnvs(config *WatchdogConfig, r *http.Request, method string) []string {
	var envs []string

	if config.cgiHeaders {
		envs = os.Environ()
		for k, v := range r.Header {
			kv := fmt.Sprintf("Http_%s=%s", strings.Replace(k, "-", "_", -1), v[0])
			envs = append(envs, kv)
		}

		envs = append(envs, fmt.Sprintf("Http_Method=%s", method))
		envs = append(envs, fmt.Sprintf("Http_ContentLength=%d", r.ContentLength))

		if config.writeDebug {
			log.Println("Query ", r.URL.RawQuery)
		}
		if len(r.URL.RawQuery) > 0 {
			envs = append(envs, fmt.Sprintf("Http_Query=%s", r.URL.RawQuery))
		}

		if config.writeDebug {
			log.Println("Path ", r.URL.Path)
		}
		if len(r.URL.Path) > 0 {
			envs = append(envs, fmt.Sprintf("Http_Path=%s", r.URL.Path))
		}

	}

	return envs
}

func makeRequestHandler(config *WatchdogConfig) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case
			"POST",
			"PUT",
			"DELETE",
			"UPDATE":
			pipeRequest(config, w, r, r.Method, true)
			break
		case
			"GET":
			pipeRequest(config, w, r, r.Method, false)
			break
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)

		}
	}
}

func main() {
	osEnv := types.OsEnv{}
	readConfig := ReadConfig{}
	config := readConfig.Read(osEnv)

	if len(config.faasProcess) == 0 {
		log.Panicln("Provide a valid process via fprocess environmental variable.")
		return
	}

	readTimeout := config.readTimeout
	writeTimeout := config.writeTimeout

	s := &http.Server{
		Addr:           ":8080",
		ReadTimeout:    readTimeout,
		WriteTimeout:   writeTimeout,
		MaxHeaderBytes: 1 << 20, // Max header of 1MB
	}

	http.HandleFunc("/", makeRequestHandler(&config))

	if config.suppressLock == false {
		path := filepath.Join(os.TempDir(), ".lock")
		log.Printf("Writing lock-file to: %s\n", path)
		writeErr := ioutil.WriteFile(path, []byte{}, 0660)
		if writeErr != nil {
			log.Panicf("Cannot write %s. To disable lock-file set env suppress_lock=true.\n Error: %s.\n", path, writeErr.Error())
		}
	} else {
		log.Println("Warning: \"suppress_lock\" is enabled. No automated health-checks will be in place for your function.")
	}

	log.Fatal(s.ListenAndServe())
}
<|endoftext|>


<commit_before>package cmd

import (
	"strings"

	"github.com/Originate/git-town/src/git"
	"github.com/Originate/git-town/src/prompt"
	"github.com/Originate/git-town/src/script"
	"github.com/Originate/git-town/src/steps"
	"github.com/Originate/git-town/src/util"

	"github.com/spf13/cobra"
)

type shipConfig struct {
	InitialBranch       string
	IsTargetBranchLocal bool
	TargetBranch        string
}

var commitMessage string

var shipCmd = &cobra.Command{
	Use:   "ship",
	Short: "Deliver a completed feature branch",
	Long: `Deliver a completed feature branch

Squash-merges the current branch, or <branch_name> if given,
into the main branch, resulting in linear history on the main branch.

- syncs the main branch
- pulls remote updates for <branch_name>
- merges the main branch into <branch_name>
- squash-merges <branch_name> into the main branch
  with commit message specified by the user
- pushes the main branch to the remote repository
- deletes <branch_name> from the local and remote repositories

Only shipping of direct children of the main branch is allowed.
To ship a nested child branch, all ancestor branches have to be shipped or killed.`,
	Run: func(cmd *cobra.Command, args []string) {
		git.EnsureIsRepository()
		prompt.EnsureIsConfigured()
		steps.Run(steps.RunOptions{
			CanSkip:              func() bool { return false },
			Command:              "ship",
			IsAbort:              abortFlag,
			IsContinue:           continueFlag,
			IsSkip:               false,
			IsUndo:               undoFlag,
			SkipMessageGenerator: func() string { return "" },
			StepListGenerator: func() steps.StepList {
				config := checkShipPreconditions(args)
				return getShipStepList(config)
			},
		})
	},
	PreRunE: func(cmd *cobra.Command, args []string) error {
		return validateMaxArgs(args, 1)
	},
}

func checkShipPreconditions(args []string) (result shipConfig) {
	result.InitialBranch = git.GetCurrentBranchName()
	if len(args) == 0 {
		result.TargetBranch = result.InitialBranch
	} else {
		result.TargetBranch = args[0]
	}
	if result.TargetBranch == result.InitialBranch {
		git.EnsureDoesNotHaveOpenChanges("Did you mean to commit them before shipping?")
	}
	if git.HasRemote("origin") {
		script.Fetch()
	}
	if result.TargetBranch != result.InitialBranch {
		git.EnsureHasBranch(result.TargetBranch)
	}
	git.EnsureIsFeatureBranch(result.TargetBranch, "Only feature branches can be shipped.")
	prompt.EnsureKnowsParentBranches([]string{result.TargetBranch})
	ensureParentBranchIsMainBranch(result.TargetBranch)
	return
}

func ensureParentBranchIsMainBranch(branchName string) {
	if git.GetParentBranch(branchName) != git.GetMainBranch() {
		ancestors := git.GetAncestorBranches(branchName)
		ancestorsWithoutMain := ancestors[1:]
		oldestAncestor := ancestorsWithoutMain[0]
		util.ExitWithErrorMessage(
			"Shipping this branch would ship "+strings.Join(ancestorsWithoutMain, ", ")+" as well.",
			"Please ship \""+oldestAncestor+"\" first.",
		)
	}
}

func getShipStepList(config shipConfig) (result steps.StepList) {
	mainBranch := git.GetMainBranch()
	areInitialAndTargetDifferent := config.TargetBranch != config.InitialBranch
	result.AppendList(steps.GetSyncBranchSteps(mainBranch))
	result.Append(steps.CheckoutBranchStep{BranchName: config.TargetBranch})
	result.Append(steps.MergeTrackingBranchStep{})
	result.Append(steps.MergeBranchStep{BranchName: mainBranch})
	result.Append(steps.EnsureHasShippableChangesStep{BranchName: config.TargetBranch})
	result.Append(steps.CheckoutBranchStep{BranchName: mainBranch})
	result.Append(steps.SquashMergeBranchStep{BranchName: config.TargetBranch, CommitMessage: commitMessage})
	if git.HasRemote("origin") {
		result.Append(steps.PushBranchStep{BranchName: mainBranch, Undoable: true})
	}
	childBranches := git.GetChildBranches(config.TargetBranch)
	if git.HasTrackingBranch(config.TargetBranch) && len(childBranches) == 0 {
		result.Append(steps.DeleteRemoteBranchStep{BranchName: config.TargetBranch, IsTracking: true})
	}
	result.Append(steps.DeleteLocalBranchStep{BranchName: config.TargetBranch})
	result.Append(steps.DeleteParentBranchStep{BranchName: config.TargetBranch})
	for _, child := range childBranches {
		result.Append(steps.SetParentBranchStep{BranchName: child, ParentBranchName: mainBranch})
	}
	result.Append(steps.DeleteAncestorBranchesStep{})
	if areInitialAndTargetDifferent {
		result.Append(steps.CheckoutBranchStep{BranchName: config.InitialBranch})
	}
	result.Wrap(steps.WrapOptions{RunInGitRoot: true, StashOpenChanges: areInitialAndTargetDifferent})
	return
}

func init() {
	shipCmd.Flags().BoolVar(&abortFlag, "abort", false, abortFlagDescription)
	shipCmd.Flags().StringVarP(&commitMessage, "message", "m", "", "Specify the commit message for the squash commit")
	shipCmd.Flags().BoolVar(&continueFlag, "continue", false, continueFlagDescription)
	shipCmd.Flags().BoolVar(&undoFlag, "undo", false, undoFlagDescription)
	RootCmd.AddCommand(shipCmd)
}
<commit_msg>Rename shipConfig.TargetBranch to .BranchToShip<commit_after>package cmd

import (
	"strings"

	"github.com/Originate/git-town/src/git"
	"github.com/Originate/git-town/src/prompt"
	"github.com/Originate/git-town/src/script"
	"github.com/Originate/git-town/src/steps"
	"github.com/Originate/git-town/src/util"

	"github.com/spf13/cobra"
)

type shipConfig struct {
	BranchToShip        string
	InitialBranch       string
	IsTargetBranchLocal bool
}

var commitMessage string

var shipCmd = &cobra.Command{
	Use:   "ship",
	Short: "Deliver a completed feature branch",
	Long: `Deliver a completed feature branch

Squash-merges the current branch, or <branch_name> if given,
into the main branch, resulting in linear history on the main branch.

- syncs the main branch
- pulls remote updates for <branch_name>
- merges the main branch into <branch_name>
- squash-merges <branch_name> into the main branch
  with commit message specified by the user
- pushes the main branch to the remote repository
- deletes <branch_name> from the local and remote repositories

Only shipping of direct children of the main branch is allowed.
To ship a nested child branch, all ancestor branches have to be shipped or killed.`,
	Run: func(cmd *cobra.Command, args []string) {
		git.EnsureIsRepository()
		prompt.EnsureIsConfigured()
		steps.Run(steps.RunOptions{
			CanSkip:              func() bool { return false },
			Command:              "ship",
			IsAbort:              abortFlag,
			IsContinue:           continueFlag,
			IsSkip:               false,
			IsUndo:               undoFlag,
			SkipMessageGenerator: func() string { return "" },
			StepListGenerator: func() steps.StepList {
				config := checkShipPreconditions(args)
				return getShipStepList(config)
			},
		})
	},
	PreRunE: func(cmd *cobra.Command, args []string) error {
		return validateMaxArgs(args, 1)
	},
}

func checkShipPreconditions(args []string) (result shipConfig) {
	result.InitialBranch = git.GetCurrentBranchName()
	if len(args) == 0 {
		result.BranchToShip = result.InitialBranch
	} else {
		result.BranchToShip = args[0]
	}
	if result.BranchToShip == result.InitialBranch {
		git.EnsureDoesNotHaveOpenChanges("Did you mean to commit them before shipping?")
	}
	if git.HasRemote("origin") {
		script.Fetch()
	}
	if result.BranchToShip != result.InitialBranch {
		git.EnsureHasBranch(result.BranchToShip)
	}
	git.EnsureIsFeatureBranch(result.BranchToShip, "Only feature branches can be shipped.")
	prompt.EnsureKnowsParentBranches([]string{result.BranchToShip})
	ensureParentBranchIsMainBranch(result.BranchToShip)
	return
}

func ensureParentBranchIsMainBranch(branchName string) {
	if git.GetParentBranch(branchName) != git.GetMainBranch() {
		ancestors := git.GetAncestorBranches(branchName)
		ancestorsWithoutMain := ancestors[1:]
		oldestAncestor := ancestorsWithoutMain[0]
		util.ExitWithErrorMessage(
			"Shipping this branch would ship "+strings.Join(ancestorsWithoutMain, ", ")+" as well.",
			"Please ship \""+oldestAncestor+"\" first.",
		)
	}
}

func getShipStepList(config shipConfig) (result steps.StepList) {
	mainBranch := git.GetMainBranch()
	isShippingInitialBranch := config.BranchToShip == config.InitialBranch
	result.AppendList(steps.GetSyncBranchSteps(mainBranch))
	result.Append(steps.CheckoutBranchStep{BranchName: config.BranchToShip})
	result.Append(steps.MergeTrackingBranchStep{})
	result.Append(steps.MergeBranchStep{BranchName: mainBranch})
	result.Append(steps.EnsureHasShippableChangesStep{BranchName: config.BranchToShip})
	result.Append(steps.CheckoutBranchStep{BranchName: mainBranch})
	result.Append(steps.SquashMergeBranchStep{BranchName: config.BranchToShip, CommitMessage: commitMessage})
	if git.HasRemote("origin") {
		result.Append(steps.PushBranchStep{BranchName: mainBranch, Undoable: true})
	}
	childBranches := git.GetChildBranches(config.BranchToShip)
	if git.HasTrackingBranch(config.BranchToShip) && len(childBranches) == 0 {
		result.Append(steps.DeleteRemoteBranchStep{BranchName: config.BranchToShip, IsTracking: true})
	}
	result.Append(steps.DeleteLocalBranchStep{BranchName: config.BranchToShip})
	result.Append(steps.DeleteParentBranchStep{BranchName: config.BranchToShip})
	for _, child := range childBranches {
		result.Append(steps.SetParentBranchStep{BranchName: child, ParentBranchName: mainBranch})
	}
	result.Append(steps.DeleteAncestorBranchesStep{})
	if !isShippingInitialBranch {
		result.Append(steps.CheckoutBranchStep{BranchName: config.InitialBranch})
	}
	result.Wrap(steps.WrapOptions{RunInGitRoot: true, StashOpenChanges: !isShippingInitialBranch})
	return
}

func init() {
	shipCmd.Flags().BoolVar(&abortFlag, "abort", false, abortFlagDescription)
	shipCmd.Flags().StringVarP(&commitMessage, "message", "m", "", "Specify the commit message for the squash commit")
	shipCmd.Flags().BoolVar(&continueFlag, "continue", false, continueFlagDescription)
	shipCmd.Flags().BoolVar(&undoFlag, "undo", false, undoFlagDescription)
	RootCmd.AddCommand(shipCmd)
}
<|endoftext|>


<commit_before>package wkb

import (
	"encoding/binary"
	"fmt"
	"io"
)

type wkbReader func(io.Reader, binary.ByteOrder) (Geom, error)

var wkbReaders = map[uint32]wkbReader{
	wkbPoint:        pointReader,
	wkbPointZ:       pointZReader,
	wkbPointM:       pointMReader,
	wkbPointZM:      pointZMReader,
	wkbLineString:   lineStringReader,
	wkbLineStringZ:  lineStringZReader,
	wkbLineStringM:  lineStringMReader,
	wkbLineStringZM: lineStringZMReader,
	wkbPolygon:      polygonReader,
	wkbPolygonZ:     polygonZReader,
	wkbPolygonM:     polygonMReader,
	wkbPolygonZM:    polygonZMReader,
}

func readLinearRing(r io.Reader, byteOrder binary.ByteOrder) ([]Point, error) {
	var numPoints uint32
	if err := binary.Read(r, byteOrder, &numPoints); err != nil {
		return nil, err
	}
	points := make([]Point, numPoints)
	for i := uint32(0); i < numPoints; i++ {
		if err := binary.Read(r, byteOrder, &points[i]); err != nil {
			return nil, err
		}
	}
	return points, nil
}

func readLinearRingZ(r io.Reader, byteOrder binary.ByteOrder) ([]PointZ, error) {
	var numPoints uint32
	if err := binary.Read(r, byteOrder, &numPoints); err != nil {
		return nil, err
	}
	pointZs := make([]PointZ, numPoints)
	for i := uint32(0); i < numPoints; i++ {
		if err := binary.Read(r, byteOrder, &pointZs[i]); err != nil {
			return nil, err
		}
	}
	return pointZs, nil
}

func readLinearRingM(r io.Reader, byteOrder binary.ByteOrder) ([]PointM, error) {
	var numPoints uint32
	if err := binary.Read(r, byteOrder, &numPoints); err != nil {
		return nil, err
	}
	pointMs := make([]PointM, numPoints)
	for i := uint32(0); i < numPoints; i++ {
		if err := binary.Read(r, byteOrder, &pointMs[i]); err != nil {
			return nil, err
		}
	}
	return pointMs, nil
}

func readLinearRingZM(r io.Reader, byteOrder binary.ByteOrder) ([]PointZM, error) {
	var numPoints uint32
	if err := binary.Read(r, byteOrder, &numPoints); err != nil {
		return nil, err
	}
	pointZMs := make([]PointZM, numPoints)
	for i := uint32(0); i < numPoints; i++ {
		if err := binary.Read(r, byteOrder, &pointZMs[i]); err != nil {
			return nil, err
		}
	}
	return pointZMs, nil
}

func pointReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	point := Point{}
	if err := binary.Read(r, byteOrder, &point); err != nil {
		return nil, err
	}
	return point, nil
}

func pointZReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	pointZ := PointZ{}
	if err := binary.Read(r, byteOrder, &pointZ); err != nil {
		return nil, err
	}
	return pointZ, nil
}

func pointMReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	pointM := PointM{}
	if err := binary.Read(r, byteOrder, &pointM); err != nil {
		return nil, err
	}
	return pointM, nil
}

func pointZMReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	pointZM := PointZM{}
	if err := binary.Read(r, byteOrder, &pointZM); err != nil {
		return nil, err
	}
	return pointZM, nil
}

func lineStringReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	points, err := readLinearRing(r, byteOrder)
	if err != nil {
		return nil, err
	}
	return LineString{points}, nil
}

func lineStringZReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	pointZs, err := readLinearRingZ(r, byteOrder)
	if err != nil {
		return nil, err
	}
	return LineStringZ{pointZs}, nil
}

func lineStringMReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	pointMs, err := readLinearRingM(r, byteOrder)
	if err != nil {
		return nil, err
	}
	return LineStringM{pointMs}, nil
}

func lineStringZMReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	pointZMs, err := readLinearRingZM(r, byteOrder)
	if err != nil {
		return nil, err
	}
	return LineStringZM{pointZMs}, nil
}

func polygonReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	var numRings uint32
	if err := binary.Read(r, byteOrder, &numRings); err != nil {
		return nil, err
	}
	rings := make([]LinearRing, numRings)
	for i := uint32(0); i < numRings; i++ {
		points, err := readLinearRing(r, byteOrder)
		if err != nil {
			return nil, err
		}
		rings[i] = points
	}
	return Polygon{rings}, nil
}

func polygonZReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	var numRings uint32
	if err := binary.Read(r, byteOrder, &numRings); err != nil {
		return nil, err
	}
	rings := make([]LinearRingZ, numRings)
	for i := uint32(0); i < numRings; i++ {
		pointZs, err := readLinearRingZ(r, byteOrder)
		if err != nil {
			return nil, err
		}
		rings[i] = pointZs
	}
	return PolygonZ{rings}, nil
}

func polygonMReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	var numRings uint32
	if err := binary.Read(r, byteOrder, &numRings); err != nil {
		return nil, err
	}
	rings := make([]LinearRingM, numRings)
	for i := uint32(0); i < numRings; i++ {
		pointMs, err := readLinearRingM(r, byteOrder)
		if err != nil {
			return nil, err
		}
		rings[i] = pointMs
	}
	return PolygonM{rings}, nil
}

func polygonZMReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	var numRings uint32
	if err := binary.Read(r, byteOrder, &numRings); err != nil {
		return nil, err
	}
	rings := make([]LinearRingZM, numRings)
	for i := uint32(0); i < numRings; i++ {
		pointZMs, err := readLinearRingZM(r, byteOrder)
		if err != nil {
			return nil, err
		}
		rings[i] = pointZMs
	}
	return PolygonZM{rings}, nil
}

func Read(r io.Reader) (Geom, error) {

	var wkbByteOrder uint8
	if err := binary.Read(r, binary.LittleEndian, &wkbByteOrder); err != nil {
		return nil, err
	}
	var byteOrder binary.ByteOrder
	switch wkbByteOrder {
	case wkbXDR:
		byteOrder = binary.BigEndian
	case wkbNDR:
		byteOrder = binary.LittleEndian
	default:
		return nil, fmt.Errorf("invalid byte order %u", wkbByteOrder)
	}

	var wkbGeometryType uint32
	if err := binary.Read(r, byteOrder, &wkbGeometryType); err != nil {
		return nil, err
	}

	if reader, ok := wkbReaders[wkbGeometryType]; ok {
		return reader(r, byteOrder)
	} else {
		return nil, fmt.Errorf("unsupported geometry type %u", wkbGeometryType)
	}

}
<commit_msg>Improve use of binary.Read<commit_after>package wkb

import (
	"encoding/binary"
	"fmt"
	"io"
)

type wkbReader func(io.Reader, binary.ByteOrder) (Geom, error)

var wkbReaders = map[uint32]wkbReader{
	wkbPoint:        pointReader,
	wkbPointZ:       pointZReader,
	wkbPointM:       pointMReader,
	wkbPointZM:      pointZMReader,
	wkbLineString:   lineStringReader,
	wkbLineStringZ:  lineStringZReader,
	wkbLineStringM:  lineStringMReader,
	wkbLineStringZM: lineStringZMReader,
	wkbPolygon:      polygonReader,
	wkbPolygonZ:     polygonZReader,
	wkbPolygonM:     polygonMReader,
	wkbPolygonZM:    polygonZMReader,
}

func readLinearRing(r io.Reader, byteOrder binary.ByteOrder) ([]Point, error) {
	var numPoints uint32
	if err := binary.Read(r, byteOrder, &numPoints); err != nil {
		return nil, err
	}
	points := make([]Point, numPoints)
	if err := binary.Read(r, byteOrder, &points); err != nil {
		return nil, err
	}
	return points, nil
}

func readLinearRingZ(r io.Reader, byteOrder binary.ByteOrder) ([]PointZ, error) {
	var numPoints uint32
	if err := binary.Read(r, byteOrder, &numPoints); err != nil {
		return nil, err
	}
	pointZs := make([]PointZ, numPoints)
	if err := binary.Read(r, byteOrder, &pointZs); err != nil {
		return nil, err
	}
	return pointZs, nil
}

func readLinearRingM(r io.Reader, byteOrder binary.ByteOrder) ([]PointM, error) {
	var numPoints uint32
	if err := binary.Read(r, byteOrder, &numPoints); err != nil {
		return nil, err
	}
	pointMs := make([]PointM, numPoints)
	if err := binary.Read(r, byteOrder, &pointMs); err != nil {
		return nil, err
	}
	return pointMs, nil
}

func readLinearRingZM(r io.Reader, byteOrder binary.ByteOrder) ([]PointZM, error) {
	var numPoints uint32
	if err := binary.Read(r, byteOrder, &numPoints); err != nil {
		return nil, err
	}
	pointZMs := make([]PointZM, numPoints)
	if err := binary.Read(r, byteOrder, &pointZMs); err != nil {
		return nil, err
	}
	return pointZMs, nil
}

func pointReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	point := Point{}
	if err := binary.Read(r, byteOrder, &point); err != nil {
		return nil, err
	}
	return point, nil
}

func pointZReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	pointZ := PointZ{}
	if err := binary.Read(r, byteOrder, &pointZ); err != nil {
		return nil, err
	}
	return pointZ, nil
}

func pointMReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	pointM := PointM{}
	if err := binary.Read(r, byteOrder, &pointM); err != nil {
		return nil, err
	}
	return pointM, nil
}

func pointZMReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	pointZM := PointZM{}
	if err := binary.Read(r, byteOrder, &pointZM); err != nil {
		return nil, err
	}
	return pointZM, nil
}

func lineStringReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	points, err := readLinearRing(r, byteOrder)
	if err != nil {
		return nil, err
	}
	return LineString{points}, nil
}

func lineStringZReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	pointZs, err := readLinearRingZ(r, byteOrder)
	if err != nil {
		return nil, err
	}
	return LineStringZ{pointZs}, nil
}

func lineStringMReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	pointMs, err := readLinearRingM(r, byteOrder)
	if err != nil {
		return nil, err
	}
	return LineStringM{pointMs}, nil
}

func lineStringZMReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	pointZMs, err := readLinearRingZM(r, byteOrder)
	if err != nil {
		return nil, err
	}
	return LineStringZM{pointZMs}, nil
}

func polygonReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	var numRings uint32
	if err := binary.Read(r, byteOrder, &numRings); err != nil {
		return nil, err
	}
	rings := make([]LinearRing, numRings)
	for i := uint32(0); i < numRings; i++ {
		points, err := readLinearRing(r, byteOrder)
		if err != nil {
			return nil, err
		}
		rings[i] = points
	}
	return Polygon{rings}, nil
}

func polygonZReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	var numRings uint32
	if err := binary.Read(r, byteOrder, &numRings); err != nil {
		return nil, err
	}
	rings := make([]LinearRingZ, numRings)
	for i := uint32(0); i < numRings; i++ {
		pointZs, err := readLinearRingZ(r, byteOrder)
		if err != nil {
			return nil, err
		}
		rings[i] = pointZs
	}
	return PolygonZ{rings}, nil
}

func polygonMReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	var numRings uint32
	if err := binary.Read(r, byteOrder, &numRings); err != nil {
		return nil, err
	}
	rings := make([]LinearRingM, numRings)
	for i := uint32(0); i < numRings; i++ {
		pointMs, err := readLinearRingM(r, byteOrder)
		if err != nil {
			return nil, err
		}
		rings[i] = pointMs
	}
	return PolygonM{rings}, nil
}

func polygonZMReader(r io.Reader, byteOrder binary.ByteOrder) (Geom, error) {
	var numRings uint32
	if err := binary.Read(r, byteOrder, &numRings); err != nil {
		return nil, err
	}
	rings := make([]LinearRingZM, numRings)
	for i := uint32(0); i < numRings; i++ {
		pointZMs, err := readLinearRingZM(r, byteOrder)
		if err != nil {
			return nil, err
		}
		rings[i] = pointZMs
	}
	return PolygonZM{rings}, nil
}

func Read(r io.Reader) (Geom, error) {

	var wkbByteOrder uint8
	if err := binary.Read(r, binary.LittleEndian, &wkbByteOrder); err != nil {
		return nil, err
	}
	var byteOrder binary.ByteOrder
	switch wkbByteOrder {
	case wkbXDR:
		byteOrder = binary.BigEndian
	case wkbNDR:
		byteOrder = binary.LittleEndian
	default:
		return nil, fmt.Errorf("invalid byte order %u", wkbByteOrder)
	}

	var wkbGeometryType uint32
	if err := binary.Read(r, byteOrder, &wkbGeometryType); err != nil {
		return nil, err
	}

	if reader, ok := wkbReaders[wkbGeometryType]; ok {
		return reader(r, byteOrder)
	} else {
		return nil, fmt.Errorf("unsupported geometry type %u", wkbGeometryType)
	}

}
<|endoftext|>


<commit_before>// Package word2vec provides functionality for reading binary word2vec models
// and basic usage (see https://code.google.com/p/word2vec/).
package word2vec

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"io"
	"sync"

	"github.com/ziutek/blas"
)

// Model is a type which represents a word2vec Model.
type Model struct {
	dim   int
	words map[string]Vector
}

// FromReader creates a Model using the binary model data provided by the io.Reader.
func FromReader(r io.Reader) (*Model, error) {
	br := bufio.NewReader(r)
	var size, dim int
	n, err := fmt.Fscanln(r, &size, &dim)
	if err != nil {
		return nil, err
	}
	if n != 2 {
		return nil, fmt.Errorf("could not extract size/dim from binary Data")
	}

	m := &Model{
		words: make(map[string]Vector, size),
		dim:   dim,
	}

	raw := make([]float32, size*dim)

	for i := 0; i < size; i++ {
		w, err := br.ReadString(' ')
		if err != nil {
			return nil, err
		}
		w = w[:len(w)-1]

		v := Vector(raw[dim*i : m.dim*(i+1)])
		err = binary.Read(br, binary.LittleEndian, v)
		if err != nil {
			return nil, err
		}

		v.Normalise()

		_, err = br.ReadByte()
		if err != nil {
			return nil, err
		}

		m.words[w] = v
	}
	return m, nil
}

// Vector is a type which represents a word vector.
type Vector []float32

// Normalise normalises the vector in-place.
func (v Vector) Normalise() {
	w := blas.Snrm2(len(v), v, 1)
	blas.Sscal(len(v), 1/w, v, 1)
}

// Add performs v += a * u (in-place).
func (v Vector) Add(a float32, u Vector) {
	blas.Saxpy(len(v), a, u, 1, v, 1)
}

// Dot computes the dot product with u.
func (v Vector) Dot(u Vector) float32 {
	return blas.Sdot(len(v), u, 1, v, 1)
}

// NotFoundError is an error returned from Model functions when an input
// word is not in the model.
type NotFoundError struct {
	Word string
}

func (e NotFoundError) Error() string {
	return fmt.Sprintf("word not found: %v", e.Word)
}

// Expr is a type which represents a linear expresssion which can be evaluated to a vector
// by a word2vec Model.
type Expr map[string]float32

// Add appends the given word with coefficient to the expression.  If the word already exists
// in the expression, then the coefficients are added.
func (e Expr) Add(f float32, w string) {
	e[w] += f
}

// AddAll is a convenience method which adds all the words in the slice to the Expr, using the given
// coefficient.
func AddAll(e Expr, f float32, ws []string) {
	for _, w := range ws {
		e.Add(f, w)
	}
}

// Eval evaluates the Expr to a Vector using a Model.
func (e Expr) Eval(m *Model) (Vector, error) {
	if len(e) == 0 {
		return nil, fmt.Errorf("must specify at least one word to evaluate")
	}
	return m.Eval(e)
}

// Size returns the number of words in the model.
func (m *Model) Size() int {
	return len(m.words)
}

// Dim returns the dimention of the vectors in the model.
func (m *Model) Dim() int {
	return m.dim
}

// Cosine returns the similarity between the two words.
func Cosine(m *Model, x, y string) (float32, error) {
	a := Expr{}
	a.Add(1, x)

	b := Expr{}
	b.Add(1, y)

	u, err := m.Eval(a)
	if err != nil {
		return 0.0, err
	}

	v, err := m.Eval(b)
	if err != nil {
		return 0.0, err
	}
	return u.Dot(v), nil
}

// Vectors returns a mapping word -> Vector for each word in `w`,
// unknown words are ignored.
func (m *Model) Vectors(words []string) map[string]Vector {
	result := make(map[string]Vector)
	for _, w := range words {
		if v, ok := m.words[w]; ok {
			result[w] = v
		}
	}
	return result
}

// Cosine returns the cosine similarity of the given expressions.
func (m *Model) Cosine(a, b Expr) (float32, error) {
	u, err := a.Eval(m)
	if err != nil {
		return 0, err
	}

	v, err := b.Eval(m)
	if err != nil {
		return 0, err
	}
	return u.Dot(v), nil
}

// Eval constructs a vector by evaluating the expression
// vector.  Returns an error if a word is not in the model.
func (m *Model) Eval(expr Expr) (Vector, error) {
	v := Vector(make([]float32, m.dim))
	for w, c := range expr {
		u, ok := m.words[w]
		if !ok {
			return nil, &NotFoundError{w}
		}
		v.Add(c, u)
	}
	v.Normalise()
	return v, nil
}

// Match is a type which represents a pairing of a word and score indicating
// the similarity of this word against a search word.
type Match struct {
	Word  string  `json:"word"`
	Score float32 `json:"score"`
}

// CosineN computes the n most similar words to the expression.  Returns an error if the
// expression could not be evaluated.
func (m *Model) CosineN(e Expr, n int) ([]Match, error) {
	v, err := e.Eval(m)
	if err != nil {
		return nil, err
	}

	v.Normalise()
	return m.simN(v, n), nil
}

// simN is a method which returns a list of `n` most similar vectors to `v` in the model.
func (m *Model) simN(v Vector, n int) []Match {
	r := make([]Match, n)
	for w, u := range m.words {
		score := v.Dot(u)
		p := Match{w, score}
		// TODO(dhowden): MaxHeap would be better here if n is large.
		if r[n-1].Score > p.Score {
			continue
		}
		r[n-1] = p
		for j := n - 2; j >= 0; j-- {
			if r[j].Score > p.Score {
				break
			}
			r[j], r[j+1] = p, r[j]
		}
	}
	return r
}

type multiMatches struct {
	N       int
	Matches []Match
}

// MultiCosineN takes a list of expressions and computes the
// n most similar words for each.
func MultiCosineN(m *Model, exprs []Expr, n int) ([][]Match, error) {
	vecs := make([]Vector, len(exprs))
	for i, e := range exprs {
		v, err := e.Eval(m)
		if err != nil {
			return nil, err
		}
		vecs[i] = v
	}

	wg := &sync.WaitGroup{}
	wg.Add(len(vecs))
	ch := make(chan multiMatches, len(vecs))
	for i, v := range vecs {
		go func(i int, v Vector) {
			ch <- multiMatches{N: i, Matches: m.simN(v, n)}
			wg.Done()
		}(i, v)
	}
	wg.Wait()
	close(ch)

	result := make([][]Match, len(vecs))
	for r := range ch {
		result[r.N] = r.Matches
	}
	return result, nil
}
<commit_msg>Remove unused Cosine function.<commit_after>// Package word2vec provides functionality for reading binary word2vec models
// and basic usage (see https://code.google.com/p/word2vec/).
package word2vec

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"io"
	"sync"

	"github.com/ziutek/blas"
)

// Model is a type which represents a word2vec Model.
type Model struct {
	dim   int
	words map[string]Vector
}

// FromReader creates a Model using the binary model data provided by the io.Reader.
func FromReader(r io.Reader) (*Model, error) {
	br := bufio.NewReader(r)
	var size, dim int
	n, err := fmt.Fscanln(r, &size, &dim)
	if err != nil {
		return nil, err
	}
	if n != 2 {
		return nil, fmt.Errorf("could not extract size/dim from binary Data")
	}

	m := &Model{
		words: make(map[string]Vector, size),
		dim:   dim,
	}

	raw := make([]float32, size*dim)

	for i := 0; i < size; i++ {
		w, err := br.ReadString(' ')
		if err != nil {
			return nil, err
		}
		w = w[:len(w)-1]

		v := Vector(raw[dim*i : m.dim*(i+1)])
		err = binary.Read(br, binary.LittleEndian, v)
		if err != nil {
			return nil, err
		}

		v.Normalise()

		_, err = br.ReadByte()
		if err != nil {
			return nil, err
		}

		m.words[w] = v
	}
	return m, nil
}

// Vector is a type which represents a word vector.
type Vector []float32

// Normalise normalises the vector in-place.
func (v Vector) Normalise() {
	w := blas.Snrm2(len(v), v, 1)
	blas.Sscal(len(v), 1/w, v, 1)
}

// Add performs v += a * u (in-place).
func (v Vector) Add(a float32, u Vector) {
	blas.Saxpy(len(v), a, u, 1, v, 1)
}

// Dot computes the dot product with u.
func (v Vector) Dot(u Vector) float32 {
	return blas.Sdot(len(v), u, 1, v, 1)
}

// NotFoundError is an error returned from Model functions when an input
// word is not in the model.
type NotFoundError struct {
	Word string
}

func (e NotFoundError) Error() string {
	return fmt.Sprintf("word not found: %v", e.Word)
}

// Expr is a type which represents a linear expresssion which can be evaluated to a vector
// by a word2vec Model.
type Expr map[string]float32

// Add appends the given word with coefficient to the expression.  If the word already exists
// in the expression, then the coefficients are added.
func (e Expr) Add(f float32, w string) {
	e[w] += f
}

// AddAll is a convenience method which adds all the words in the slice to the Expr, using the given
// coefficient.
func AddAll(e Expr, f float32, ws []string) {
	for _, w := range ws {
		e.Add(f, w)
	}
}

// Eval evaluates the Expr to a Vector using a Model.
func (e Expr) Eval(m *Model) (Vector, error) {
	if len(e) == 0 {
		return nil, fmt.Errorf("must specify at least one word to evaluate")
	}
	return m.Eval(e)
}

// Size returns the number of words in the model.
func (m *Model) Size() int {
	return len(m.words)
}

// Dim returns the dimention of the vectors in the model.
func (m *Model) Dim() int {
	return m.dim
}

// Vectors returns a mapping word -> Vector for each word in `w`,
// unknown words are ignored.
func (m *Model) Vectors(words []string) map[string]Vector {
	result := make(map[string]Vector)
	for _, w := range words {
		if v, ok := m.words[w]; ok {
			result[w] = v
		}
	}
	return result
}

// Cosine returns the cosine similarity of the given expressions.
func (m *Model) Cosine(a, b Expr) (float32, error) {
	u, err := a.Eval(m)
	if err != nil {
		return 0, err
	}

	v, err := b.Eval(m)
	if err != nil {
		return 0, err
	}
	return u.Dot(v), nil
}

// Eval constructs a vector by evaluating the expression
// vector.  Returns an error if a word is not in the model.
func (m *Model) Eval(expr Expr) (Vector, error) {
	v := Vector(make([]float32, m.dim))
	for w, c := range expr {
		u, ok := m.words[w]
		if !ok {
			return nil, &NotFoundError{w}
		}
		v.Add(c, u)
	}
	v.Normalise()
	return v, nil
}

// Match is a type which represents a pairing of a word and score indicating
// the similarity of this word against a search word.
type Match struct {
	Word  string  `json:"word"`
	Score float32 `json:"score"`
}

// CosineN computes the n most similar words to the expression.  Returns an error if the
// expression could not be evaluated.
func (m *Model) CosineN(e Expr, n int) ([]Match, error) {
	v, err := e.Eval(m)
	if err != nil {
		return nil, err
	}

	v.Normalise()
	return m.simN(v, n), nil
}

// simN is a method which returns a list of `n` most similar vectors to `v` in the model.
func (m *Model) simN(v Vector, n int) []Match {
	r := make([]Match, n)
	for w, u := range m.words {
		score := v.Dot(u)
		p := Match{w, score}
		// TODO(dhowden): MaxHeap would be better here if n is large.
		if r[n-1].Score > p.Score {
			continue
		}
		r[n-1] = p
		for j := n - 2; j >= 0; j-- {
			if r[j].Score > p.Score {
				break
			}
			r[j], r[j+1] = p, r[j]
		}
	}
	return r
}

type multiMatches struct {
	N       int
	Matches []Match
}

// MultiCosineN takes a list of expressions and computes the
// n most similar words for each.
func MultiCosineN(m *Model, exprs []Expr, n int) ([][]Match, error) {
	vecs := make([]Vector, len(exprs))
	for i, e := range exprs {
		v, err := e.Eval(m)
		if err != nil {
			return nil, err
		}
		vecs[i] = v
	}

	wg := &sync.WaitGroup{}
	wg.Add(len(vecs))
	ch := make(chan multiMatches, len(vecs))
	for i, v := range vecs {
		go func(i int, v Vector) {
			ch <- multiMatches{N: i, Matches: m.simN(v, n)}
			wg.Done()
		}(i, v)
	}
	wg.Wait()
	close(ch)

	result := make([][]Match, len(vecs))
	for r := range ch {
		result[r.N] = r.Matches
	}
	return result, nil
}
<|endoftext|>