<commit_before>package server

import (
	"errors"

	"github.com/cashshuffle/cashshuffle/message"
)

// checkBlameMessage checks to see if the player has sent a blame.
func (pi *packetInfo) checkBlameMessage() error {
	if len(pi.message.Packet) != 1 {
		return nil
	}

	pkt := pi.message.Packet[0]
	packet := pkt.GetPacket()

	if packet.Message == nil {
		return nil
	}

	if packet.Message.Blame == nil {
		return nil
	}

	validBlame := false
	validBlamereasons := []message.Reason{
		message.Reason_INSUFFICIENTFUNDS,
		message.Reason_DOUBLESPEND,
		message.Reason_EQUIVOCATIONFAILURE,
		message.Reason_SHUFFLEFAILURE,
		message.Reason_SHUFFLEANDEQUIVOCATIONFAILURE,
		message.Reason_INVALIDSIGNATURE,
		message.Reason_MISSINGOUTPUT,
		message.Reason_INVALIDSIGNATURE,
		message.Reason_INVALIDFORMAT,
	}

	for _, reason := range validBlamereasons {
		if packet.Message.Blame.Reason == reason {
			validBlame = true
		}
	}

	if validBlame {
		accusedKey := packet.Message.Blame.Accused.String()
		accused := pi.tracker.playerByVerificationKey(accusedKey)

		if accused == nil {
			return nil
		}

		blamer := pi.tracker.playerByConnection(pi.conn)
		if blamer == nil {
			return nil
		}

		if accused.pool != blamer.pool {
			return errors.New("invalid ban")
		}

		added := accused.addBlame(blamer.verificationKey)
		if !added {
			return nil
		}

		if pi.tracker.bannedByPool(accused, true) {
			pi.tracker.increaseBanScore(accused.conn)
			pi.tracker.decreasePoolVoters(accused.pool)
			pi.tracker.addDenyIPMatch(accused.conn, accused.pool)
		}
	}

	return nil
}
<commit_msg>Avoid reallocating valid blame reasons<commit_after>package server

import (
	"errors"

	"github.com/cashshuffle/cashshuffle/message"
)

var validBlamereasons = []message.Reason{
	message.Reason_INSUFFICIENTFUNDS,
	message.Reason_DOUBLESPEND,
	message.Reason_EQUIVOCATIONFAILURE,
	message.Reason_SHUFFLEFAILURE,
	message.Reason_SHUFFLEANDEQUIVOCATIONFAILURE,
	message.Reason_INVALIDSIGNATURE,
	message.Reason_MISSINGOUTPUT,
	message.Reason_INVALIDSIGNATURE,
	message.Reason_INVALIDFORMAT,
}

// checkBlameMessage checks to see if the player has sent a blame.
func (pi *packetInfo) checkBlameMessage() error {
	if len(pi.message.Packet) != 1 {
		return nil
	}

	pkt := pi.message.Packet[0]
	packet := pkt.GetPacket()

	if packet.Message == nil {
		return nil
	}

	if packet.Message.Blame == nil {
		return nil
	}

	validBlame := false

	for _, reason := range validBlamereasons {
		if packet.Message.Blame.Reason == reason {
			validBlame = true
		}
	}

	if validBlame {
		accusedKey := packet.Message.Blame.Accused.String()
		accused := pi.tracker.playerByVerificationKey(accusedKey)

		if accused == nil {
			return nil
		}

		blamer := pi.tracker.playerByConnection(pi.conn)
		if blamer == nil {
			return nil
		}

		if accused.pool != blamer.pool {
			return errors.New("invalid ban")
		}

		added := accused.addBlame(blamer.verificationKey)
		if !added {
			return nil
		}

		if pi.tracker.bannedByPool(accused, true) {
			pi.tracker.increaseBanScore(accused.conn)
			pi.tracker.decreasePoolVoters(accused.pool)
			pi.tracker.addDenyIPMatch(accused.conn, accused.pool)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package imageserver

import (
	"testing"
)

func TestParametersSetGet(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", "bar")
	value, err := parameters.Get("foo")
	if err != nil {
		t.Fatal(err)
	}
	if value != "bar" {
		t.Fatal("not equals")
	}
}

func TestParametersHas(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", "bar")
	if !parameters.Has("foo") {
		t.Fatal("key does not exist")
	}
	if parameters.Has("xxx") {
		t.Fatal("key exists")
	}
}

func TestParametersEmpty(t *testing.T) {
	parameters := make(Parameters)
	if !parameters.Empty() {
		t.Fatal("not empty")
	}
	parameters.Set("foo", "bar")
	if parameters.Empty() {
		t.Fatal("empty")
	}
}

func TestParametersGetErrorMiss(t *testing.T) {
	parameters := make(Parameters)
	_, err := parameters.Get("foo")
	if err == nil {
		t.Fatal("no miss")
	}
}

func TestParametersGetString(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", "bar")
	value, err := parameters.GetString("foo")
	if err != nil {
		t.Fatal(err)
	}
	if value != "bar" {
		t.Fatal("not equals")
	}
}

func TestParametersGetStringErrorWrongType(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", 666)
	_, err := parameters.GetString("foo")
	if err == nil {
		t.Fatal("no error")
	}
}

func TestParametersGetInt(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", 7)
	value, err := parameters.GetInt("foo")
	if err != nil {
		t.Fatal(err)
	}
	if value != 7 {
		t.Fatal("not equals")
	}
}

func TestParametersGetIntErrorWrongType(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", "bar")
	_, err := parameters.GetInt("foo")
	if err == nil {
		t.Fatal("no error")
	}
}

func TestParametersGetBool(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", true)
	value, err := parameters.GetBool("foo")
	if err != nil {
		t.Fatal(err)
	}
	if value != true {
		t.Fatal("Not equals")
	}
}

func TestParametersGetBoolErrorWrongType(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", "bar")
	_, err := parameters.GetBool("foo")
	if err == nil {
		t.Fatal("no error")
	}
}

func TestParametersGetParameters(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", make(Parameters))
	_, err := parameters.GetParameters("foo")
	if err != nil {
		t.Fatal(err)
	}
}

func TestParametersGetParametersErrorWrongType(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", "bar")
	_, err := parameters.GetParameters("foo")
	if err == nil {
		t.Fatal("no error")
	}
}
<commit_msg>improve test coverage in Parameters<commit_after>package imageserver

import (
	"testing"
)

func TestParametersSetGet(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", "bar")
	value, err := parameters.Get("foo")
	if err != nil {
		t.Fatal(err)
	}
	if value != "bar" {
		t.Fatal("not equals")
	}
}

func TestParametersHas(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", "bar")
	if !parameters.Has("foo") {
		t.Fatal("key does not exist")
	}
	if parameters.Has("xxx") {
		t.Fatal("key exists")
	}
}

func TestParametersEmpty(t *testing.T) {
	parameters := make(Parameters)
	if !parameters.Empty() {
		t.Fatal("not empty")
	}
	parameters.Set("foo", "bar")
	if parameters.Empty() {
		t.Fatal("empty")
	}
}

func TestParametersGetErrorMiss(t *testing.T) {
	parameters := make(Parameters)
	_, err := parameters.Get("foo")
	if err == nil {
		t.Fatal("no miss")
	}
}

func TestParametersGetString(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", "bar")
	value, err := parameters.GetString("foo")
	if err != nil {
		t.Fatal(err)
	}
	if value != "bar" {
		t.Fatal("not equals")
	}
}

func TestParametersGetStringErrorMiss(t *testing.T) {
	parameters := make(Parameters)
	_, err := parameters.GetString("foo")
	if err == nil {
		t.Fatal("no miss")
	}
}

func TestParametersGetStringErrorWrongType(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", 666)
	_, err := parameters.GetString("foo")
	if err == nil {
		t.Fatal("no error")
	}
}

func TestParametersGetInt(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", 7)
	value, err := parameters.GetInt("foo")
	if err != nil {
		t.Fatal(err)
	}
	if value != 7 {
		t.Fatal("not equals")
	}
}

func TestParametersGetIntErrorMiss(t *testing.T) {
	parameters := make(Parameters)
	_, err := parameters.GetInt("foo")
	if err == nil {
		t.Fatal("no miss")
	}
}

func TestParametersGetIntErrorWrongType(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", "bar")
	_, err := parameters.GetInt("foo")
	if err == nil {
		t.Fatal("no error")
	}
}

func TestParametersGetBool(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", true)
	value, err := parameters.GetBool("foo")
	if err != nil {
		t.Fatal(err)
	}
	if value != true {
		t.Fatal("Not equals")
	}
}

func TestParametersGetBoolErrorMiss(t *testing.T) {
	parameters := make(Parameters)
	_, err := parameters.GetBool("foo")
	if err == nil {
		t.Fatal("no miss")
	}
}

func TestParametersGetBoolErrorWrongType(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", "bar")
	_, err := parameters.GetBool("foo")
	if err == nil {
		t.Fatal("no error")
	}
}

func TestParametersGetParameters(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", make(Parameters))
	_, err := parameters.GetParameters("foo")
	if err != nil {
		t.Fatal(err)
	}
}

func TestParametersGetParametersErrorMiss(t *testing.T) {
	parameters := make(Parameters)
	_, err := parameters.GetParameters("foo")
	if err == nil {
		t.Fatal("no miss")
	}
}

func TestParametersGetParametersErrorWrongType(t *testing.T) {
	parameters := make(Parameters)
	parameters.Set("foo", "bar")
	_, err := parameters.GetParameters("foo")
	if err == nil {
		t.Fatal("no error")
	}
}
<|endoftext|>


<commit_before>package lib

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
)

type Action interface {
	getName() string
	getHint() string
	firstUp() Action
	getQuestions() Questions
	nextQuestion() *Question
	askQuestion()
}

const (
	start_action, help_action = "/start", "/help"

	say_action      = "/say"
	say_action_hint = "/say <message>"

	chat_action = "/chat"

	review_action      = "/review"
	review_action_hint = "/review <days>"

	remind_action      = "/remind"
	remind_action_hint = "/remind in <days> to <message>"
)

func NewAction(in *Incoming, s *session, actionName string) Action {

	if s != nil {
		s.User = in.msg.Sender
	}

	cmd := actionName

	if in.isCmd() {
		cmd = in.getCmd()
	}

	if cmd == say_action {
		return SayAction{in: in, s: s}
	} else if cmd == remind_action {
		return &RemindAction{in: in, s: s}
	} else if cmd == review_action {
		return &ReviewAction{in: in, s: s}
	} else if cmd == chat_action {
		return &ChatAction{in: in, s: s}
	} else if in.isSticker() {
		return &StickerChatAction{in: in, s: s}
	}
	return &HelpAction{in: in, s: s}
}

func load(name string, q interface{}) {

	if strings.Contains(name, "/") {
		name = strings.Split(name, "/")[1]
	}

	absPath, _ := filepath.Abs(fmt.Sprintf("config/%s.json", name))

	log.Println("path ", absPath)

	file, err := os.Open(absPath)
	if err != nil {
		log.Panic("could not load QandA language file ", err.Error())
	}

	err = json.NewDecoder(file).Decode(&q)
	if err != nil {
		log.Panic("could not decode QandA ", err.Error())
	}

	return
}

type SayAction struct {
	s  *session
	in *Incoming
}

func (a SayAction) getName() string {
	return say_action
}
func (a SayAction) getHint() string {
	return say_action_hint
}
func (a SayAction) firstUp() Action {
	a.s.save(a.in.getNote())
	return a
}

func (a SayAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}

	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a SayAction) askQuestion() {
	q := a.nextQuestion()
	a.s.send(q.Context...)
	a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
}

func (a SayAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	if a.in.hasSubmisson() {
		load(default_script, &q)
		return q.Questions
	}
	load(a.getName(), &q)
	return q.Questions
}

type HelpAction struct {
	s  *session
	in *Incoming
	q  struct {
		Questions `json:"QandA"`
	}
}

func (a HelpAction) getName() string {
	return say_action
}
func (a HelpAction) getHint() string {
	return say_action_hint
}
func (a HelpAction) firstUp() Action {
	log.Println("help first up")
	helpInfo := fmt.Sprintf("%s %s %s %s ",
		fmt.Sprintf("Hey %s! We can't doFirst it all but we can:\n\n", a.in.sender().Username),
		chat_action+" - to have a *quick chat* about what your up-to \n\n",
		say_action_hint+" - to say *anything* thats on your mind \n\n",
		review_action_hint+" - to review what has been happening \n\n",
	)
	a.s.send(helpInfo)
	return a
}

func (a HelpAction) nextQuestion() *Question {
	return nil
}

func (a HelpAction) getQuestions() Questions {
	return nil
}

func (a HelpAction) askQuestion() {
	return
}

type ChatAction struct {
	s  *session
	in *Incoming
}

func (a ChatAction) getName() string {
	return chat_action
}
func (a ChatAction) getHint() string {
	return ""
}
func (a ChatAction) firstUp() Action {
	//nothing to doFirst
	return a
}

func (a ChatAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	load(a.getName(), &q)
	return q.Questions
}

func (a ChatAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a ChatAction) askQuestion() {
	q := a.nextQuestion()
	a.s.send(q.Context...)
	a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	return
}

type ReviewAction struct {
	s  *session
	in *Incoming
	q  struct {
		Questions `json:"QandA"`
	}
}

func (a ReviewAction) getName() string {
	return review_action
}
func (a ReviewAction) getHint() string {
	return review_action_hint
}
func (a ReviewAction) firstUp() Action {
	log.Println("doFirsting review ...")
	n := a.s.getNotes(a.in.msg)

	saidTxt := fmt.Sprintf("%s you said: \n\n %s", a.in.sender().Username, n.FilterBy(said_tag).ToString())
	a.s.send(saidTxt)
	talkedTxt := fmt.Sprintf("%s we talked about: \n\n %s", a.in.sender().Username, n.FilterBy(chat_tag).ToString())
	a.s.send(talkedTxt)
	return a
}

func (a ReviewAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	load(a.getName(), &q)

	return q.Questions
}

func (a ReviewAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a ReviewAction) askQuestion() {
	q := a.nextQuestion()
	a.s.send(q.Context...)
	a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	return
}

type RemindAction struct {
	s  *session
	in *Incoming
}

func (a RemindAction) getName() string {
	return remind_action
}
func (a RemindAction) getHint() string {
	return remind_action_hint
}
func (a RemindAction) firstUp() Action {
	log.Println("remind me ...")
	a.s.save(a.in.getNote())
	return a
}

func (a RemindAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	if a.in.hasSubmisson() {
		load(default_script, &q)
		return q.Questions
	}
	load(a.getName(), &q)

	return q.Questions
}

func (a RemindAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a RemindAction) askQuestion() {
	q := a.nextQuestion()
	a.s.send(q.Context...)
	a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	return
}

type StickerChatAction struct {
	s        *session
	in       *Incoming
	stickers Stickers
}

func (a StickerChatAction) getName() string {
	return chat_action
}
func (a StickerChatAction) getHint() string {
	return ""
}

func (a StickerChatAction) firstUp() Action {
	a.stickers = LoadKnownStickers()
	return a
}

func (a StickerChatAction) getQuestions() Questions {
	var q struct {
		Questions `json:"QandA"`
	}
	load(a.getName(), &q)
	return q.Questions
}

func (a StickerChatAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isSticker() {

		sticker := a.stickers.FindSticker(a.in.msg.Sticker.FileID)
		a.in.msg.Text = sticker.Meaning
		next, save := q.nextFrom(sticker.Ids...)
		if save {
			a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
		}
		return next
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a StickerChatAction) askQuestion() {
	q := a.nextQuestion()
	a.s.send(q.Context...)
	a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	return
}
<commit_msg>actions pathing fix<commit_after>package lib

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
)

type Action interface {
	getName() string
	getHint() string
	firstUp() Action
	getQuestions() Questions
	nextQuestion() *Question
	askQuestion()
}

const (
	start_action, help_action = "/start", "/help"

	say_action      = "/say"
	say_action_hint = "/say <message>"

	chat_action = "/chat"

	review_action      = "/review"
	review_action_hint = "/review <days>"

	remind_action      = "/remind"
	remind_action_hint = "/remind in <days> to <message>"
)

func NewAction(in *Incoming, s *session, actionName string) Action {

	if s != nil {
		s.User = in.msg.Sender
	}

	cmd := actionName

	if in.isCmd() {
		cmd = in.getCmd()
	}

	if cmd == say_action {
		return SayAction{in: in, s: s}
	} else if cmd == remind_action {
		return &RemindAction{in: in, s: s}
	} else if cmd == review_action {
		return &ReviewAction{in: in, s: s}
	} else if cmd == chat_action {
		return &ChatAction{in: in, s: s}
	} else if in.isSticker() {
		return &StickerChatAction{in: in, s: s}
	}
	return &HelpAction{in: in, s: s}
}

func load(name string, q interface{}) {

	if strings.Contains(name, "/") {
		name = strings.Split(name, "/")[1]
	}

	absPath, _ := filepath.Abs(fmt.Sprintf("config/%s.json", name))

	log.Println("QandA", absPath)

	file, err := os.Open(absPath)
	if err != nil {
		log.Println("could not load QandA language file", err.Error())
		absPath, _ = filepath.Abs(fmt.Sprintf("lib/config/%s.json", name))
		log.Println("QandA path ", absPath)

		file, err = os.Open(absPath)
	}

	err = json.NewDecoder(file).Decode(&q)
	if err != nil {
		log.Panic("could not decode QandA ", err.Error())
	}

	return
}

type SayAction struct {
	s  *session
	in *Incoming
}

func (a SayAction) getName() string {
	return say_action
}
func (a SayAction) getHint() string {
	return say_action_hint
}
func (a SayAction) firstUp() Action {
	a.s.save(a.in.getNote())
	return a
}

func (a SayAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}

	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a SayAction) askQuestion() {
	q := a.nextQuestion()
	a.s.send(q.Context...)
	a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
}

func (a SayAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	if a.in.hasSubmisson() {
		load(default_script, &q)
		return q.Questions
	}
	load(a.getName(), &q)
	return q.Questions
}

type HelpAction struct {
	s  *session
	in *Incoming
	q  struct {
		Questions `json:"QandA"`
	}
}

func (a HelpAction) getName() string {
	return say_action
}
func (a HelpAction) getHint() string {
	return say_action_hint
}
func (a HelpAction) firstUp() Action {
	log.Println("help first up")
	helpInfo := fmt.Sprintf("%s %s %s %s ",
		fmt.Sprintf("Hey %s! We can't doFirst it all but we can:\n\n", a.in.sender().Username),
		chat_action+" - to have a *quick chat* about what your up-to \n\n",
		say_action_hint+" - to say *anything* thats on your mind \n\n",
		review_action_hint+" - to review what has been happening \n\n",
	)
	a.s.send(helpInfo)
	return a
}

func (a HelpAction) nextQuestion() *Question {
	return nil
}

func (a HelpAction) getQuestions() Questions {
	return nil
}

func (a HelpAction) askQuestion() {
	return
}

type ChatAction struct {
	s  *session
	in *Incoming
}

func (a ChatAction) getName() string {
	return chat_action
}
func (a ChatAction) getHint() string {
	return ""
}
func (a ChatAction) firstUp() Action {
	//nothing to doFirst
	return a
}

func (a ChatAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	load(a.getName(), &q)
	return q.Questions
}

func (a ChatAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a ChatAction) askQuestion() {
	q := a.nextQuestion()
	a.s.send(q.Context...)
	a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	return
}

type ReviewAction struct {
	s  *session
	in *Incoming
	q  struct {
		Questions `json:"QandA"`
	}
}

func (a ReviewAction) getName() string {
	return review_action
}
func (a ReviewAction) getHint() string {
	return review_action_hint
}
func (a ReviewAction) firstUp() Action {
	log.Println("doFirsting review ...")
	n := a.s.getNotes(a.in.msg)

	saidTxt := fmt.Sprintf("%s you said: \n\n %s", a.in.sender().Username, n.FilterBy(said_tag).ToString())
	a.s.send(saidTxt)
	talkedTxt := fmt.Sprintf("%s we talked about: \n\n %s", a.in.sender().Username, n.FilterBy(chat_tag).ToString())
	a.s.send(talkedTxt)
	return a
}

func (a ReviewAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	load(a.getName(), &q)

	return q.Questions
}

func (a ReviewAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a ReviewAction) askQuestion() {
	q := a.nextQuestion()
	a.s.send(q.Context...)
	a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	return
}

type RemindAction struct {
	s  *session
	in *Incoming
}

func (a RemindAction) getName() string {
	return remind_action
}
func (a RemindAction) getHint() string {
	return remind_action_hint
}
func (a RemindAction) firstUp() Action {
	log.Println("remind me ...")
	a.s.save(a.in.getNote())
	return a
}

func (a RemindAction) getQuestions() Questions {

	var q struct {
		Questions `json:"QandA"`
	}

	if a.in.hasSubmisson() {
		load(default_script, &q)
		return q.Questions
	}
	load(a.getName(), &q)

	return q.Questions
}

func (a RemindAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isCmd() {
		return q.First()
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a RemindAction) askQuestion() {
	q := a.nextQuestion()
	a.s.send(q.Context...)
	a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	return
}

type StickerChatAction struct {
	s        *session
	in       *Incoming
	stickers Stickers
}

func (a StickerChatAction) getName() string {
	return chat_action
}
func (a StickerChatAction) getHint() string {
	return ""
}

func (a StickerChatAction) firstUp() Action {
	a.stickers = LoadKnownStickers()
	return a
}

func (a StickerChatAction) getQuestions() Questions {
	var q struct {
		Questions `json:"QandA"`
	}
	load(a.getName(), &q)
	return q.Questions
}

func (a StickerChatAction) nextQuestion() *Question {

	q := a.getQuestions()

	if a.in.isSticker() {

		sticker := a.stickers.FindSticker(a.in.msg.Sticker.FileID)
		a.in.msg.Text = sticker.Meaning
		next, save := q.nextFrom(sticker.Ids...)
		if save {
			a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
		}
		return next
	}
	next, save := q.next(a.in.msg.Text)
	if save {
		a.s.save(a.in.getNote(a.getName(), next.RelatesTo.SaveTag))
	}
	return next
}

func (a StickerChatAction) askQuestion() {
	q := a.nextQuestion()
	a.s.send(q.Context...)
	a.s.sendWithKeyboard(q.QuestionText, q.makeKeyboard())
	return
}
<|endoftext|>


<commit_before>package server

import (
	"net/http"
	"strings"

	"github.com/golang/glog"
	"github.com/namely/broadway/deployment"
	"github.com/namely/broadway/env"
	"github.com/namely/broadway/instance"
	"github.com/namely/broadway/notification"
	"github.com/namely/broadway/services"
	"github.com/namely/broadway/store"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
)

// Server provides an HTTP interface to manipulate Playbooks and Instances
type Server struct {
	store      store.Store
	slackToken string
	playbooks  map[string]*deployment.Playbook
	manifests  map[string]*deployment.Manifest
	deployer   deployment.Deployer
	engine     *gin.Engine
}

const commandHint string = `/broadway help: This message
/broadway deploy myPlaybookID myInstanceID: Deploy a new instance`

// ErrorResponse represents a JSON response to be returned in failure cases
type ErrorResponse map[string]string

var (
	// BadRequestError represents a JSON response for status 400
	BadRequestError = ErrorResponse{"error": "Bad Request"}
	// UnauthorizedError represents a JSON response for status 401
	UnauthorizedError = ErrorResponse{"error": "Unauthorized"}
	// NotFoundError represents a JSON response for status 404
	NotFoundError = ErrorResponse{"error": "Not Found"}
	// InternalError represents a JSON response for status 500
	InternalError = ErrorResponse{"error": "Internal Server Error"}
)

// CustomError creates an ErrorResponse with a custom message
func CustomError(message string) ErrorResponse {
	return ErrorResponse{"error": message}
}

// New instantiates a new Server and binds its handlers. The Server will look
// for playbooks and instances in store `s`
func New(s store.Store) *Server {
	srvr := &Server{
		store:      s,
		slackToken: env.SlackToken,
	}
	srvr.setupHandlers()
	return srvr
}

// Init initializes manifests and playbooks for the server.
func (s *Server) Init() {
	ms := services.NewManifestService(env.ManifestsPath)

	var err error
	s.manifests, err = ms.LoadManifestFolder()
	if err != nil {
		glog.Fatal(err)
	}

	s.playbooks = deployment.AllPlaybooks
	glog.Infof("Server Playbooks: %+v", s.playbooks)
}

func (s *Server) setupHandlers() {
	s.engine = gin.Default()
	gin.SetMode(gin.ReleaseMode) // Comment this to use debug mode for more verbose output
	// Define routes:
	s.engine.POST("/command", s.postCommand)
	s.engine.GET("/command", s.getCommand)
	// Protect subsequent routes with middleware:
	s.engine.Use(authMiddleware)
	s.engine.GET("/", s.home)
	s.engine.POST("/instances", s.createInstance)
	s.engine.GET("/instance/:playbookID/:instanceID", s.getInstance)
	s.engine.GET("/instances/:playbookID", s.getInstances)
	s.engine.GET("/status/:playbookID/:instanceID", s.getStatus)
	s.engine.POST("/deploy/:playbookID/:instanceID", s.deployInstance)
	s.engine.DELETE("/instances/:playbookID/:instanceID", s.deleteInstance)
}

// Handler returns a reference to the Gin engine that powers Server
func (s *Server) Handler() http.Handler {
	return s.engine
}

// Run starts the server on the specified address
func (s *Server) Run(addr ...string) error {
	return s.engine.Run(addr...)
}

func authMiddleware(c *gin.Context) {
	a := c.Request.Header.Get("Authorization")
	a = strings.TrimPrefix(a, "Bearer ")
	if len(a) == 0 || a != env.AuthBearerToken {
		glog.Infof("Auth failure for %s\nExpected: %s Actual: %s\n", c.Request.URL.Path, env.AuthBearerToken, a)
		c.String(http.StatusUnauthorized, "Wrong or Missing Authorization")
		c.AbortWithStatus(http.StatusUnauthorized)
		return
	}
	c.Next()
}

func (s *Server) home(c *gin.Context) {
	c.String(http.StatusOK, "Welcome to Broadway!")
}

func (s *Server) createInstance(c *gin.Context) {
	var i instance.Instance
	if err := c.BindJSON(&i); err != nil {
		glog.Error(err)
		c.JSON(http.StatusBadRequest, CustomError("Missing: "+err.Error()))
		return
	}

	service := services.NewInstanceService(store.New())
	createdInstance, err := service.Create(&i)

	if err != nil {
		glog.Error(err)
		c.JSON(http.StatusInternalServerError, InternalError)
		return
	}

	c.JSON(http.StatusCreated, createdInstance)
}

func (s *Server) getInstance(c *gin.Context) {
	service := services.NewInstanceService(s.store)
	i, err := service.Show(c.Param("playbookID"), c.Param("instanceID"))

	if err != nil {
		switch err.(type) {
		case instance.NotFound:
			c.JSON(http.StatusNotFound, NotFoundError)
			return
		default:
			c.JSON(http.StatusInternalServerError, InternalError)
			return
		}
	}
	c.JSON(http.StatusOK, i)
}

func (s *Server) getInstances(c *gin.Context) {
	service := services.NewInstanceService(s.store)
	instances, err := service.AllWithPlaybookID(c.Param("playbookID"))
	if err != nil {
		c.JSON(http.StatusInternalServerError, InternalError)
		return
	}
	c.JSON(http.StatusOK, instances)
	return
}

func (s *Server) getStatus(c *gin.Context) {
	service := services.NewInstanceService(s.store)
	i, err := service.Show(c.Param("playbookID"), c.Param("instanceID"))

	if err != nil {
		switch err.(type) {
		case instance.NotFound:
			c.JSON(http.StatusNotFound, NotFoundError)
			return
		default:
			c.JSON(http.StatusInternalServerError, InternalError)
			return
		}
	}
	c.JSON(http.StatusOK, map[string]string{
		"status": string(i.Status),
	})
}

func (s *Server) getCommand(c *gin.Context) {
	ssl := c.Query("ssl_check")
	glog.Info(ssl)
	if ssl == "1" {
		c.String(http.StatusOK, "")
	} else {
		c.String(http.StatusBadRequest, "Use POST /command")
	}
}

// SlackCommand represents the unmarshalled JSON post data from Slack
type SlackCommand struct {
	Token       string `form:"token"`
	TeamID      string `form:"team_id"`
	TeamDomain  string `form:"team_domain"`
	ChannelID   string `form:"channel_id"`
	ChannelName string `form:"channel_name"`
	UserID      string `form:"user_id"`
	UserName    string `form:"user_name"`
	Command     string `form:"command"`
	Text        string `form:"text"`
	ResponseURL string `form:"response_url"`
}

func (s *Server) postCommand(c *gin.Context) {
	var form SlackCommand
	if err := c.BindWith(&form, binding.Form); err != nil {
		glog.Error(err)
		c.JSON(http.StatusInternalServerError, InternalError)
		return
	}

	if form.Token != s.slackToken {
		glog.Errorf("Token mismatch, actual: %s, expected: %s\n", form.Token, s.slackToken)
		c.JSON(http.StatusUnauthorized, UnauthorizedError)
		return
	}

	is := services.NewInstanceService(s.store)
	slackCommand := services.BuildSlackCommand(form.Text, is, s.playbooks)
	glog.Infof("Running command: %s", form.Text)
	msg, err := slackCommand.Execute()
	if err != nil {
		c.JSON(http.StatusOK, err)
		return
	}

	// Craft a Slack payload for an ephemeral message:
	j := notification.NewMessage(true, msg)
	c.JSON(http.StatusOK, j)
	return
}

func deploy(s *Server, pID string, ID string) (*instance.Instance, error) {
	is := services.NewInstanceService(s.store)
	i, err := is.Show(pID, ID)
	if err != nil {
		return nil, err
	}

	ds := services.NewDeploymentService(s.store, s.playbooks, s.manifests)

	err = ds.DeployAndNotify(i)
	if err != nil {
		return nil, err
	}
	return i, nil
}

func del(s *Server, pID string, ID string) (*instance.Instance, error) {
	is := services.NewInstanceService(s.store)
	i, err := is.Show(pID, ID)
	if err != nil {
		return nil, err
	}

	ds := services.NewDeploymentService(s.store, s.playbooks, s.manifests)

	err = ds.DeleteAndNotify(i)
	if err != nil {
		return nil, err
	}
	return i, nil
}

func (s *Server) deployInstance(c *gin.Context) {
	i, err := deploy(s, c.Param("playbookID"), c.Param("instanceID"))
	if err != nil {
		glog.Error(err)
		switch err.(type) {
		case instance.NotFound:
			c.JSON(http.StatusNotFound, NotFoundError)
			return
		default:
			c.JSON(http.StatusInternalServerError, InternalError)
			return
		}
	}
	c.JSON(http.StatusOK, i)
}

func (s *Server) deleteInstance(c *gin.Context) {
	_, err := del(s, c.Param("playbookID"), c.Param("instanceID"))
	if err != nil {
		glog.Error(err)
		switch err.(type) {
		case instance.NotFound:
			c.JSON(http.StatusNotFound, NotFoundError)
			return
		default:
			c.JSON(http.StatusInternalServerError, InternalError)
			return
		}
	}
	c.JSON(http.StatusOK, map[string]string{"message": "Instance successfully deleted"})
}
<commit_msg>showing slash command in channel (#85)<commit_after>package server

import (
	"net/http"
	"strings"

	"github.com/golang/glog"
	"github.com/namely/broadway/deployment"
	"github.com/namely/broadway/env"
	"github.com/namely/broadway/instance"
	"github.com/namely/broadway/notification"
	"github.com/namely/broadway/services"
	"github.com/namely/broadway/store"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
)

// Server provides an HTTP interface to manipulate Playbooks and Instances
type Server struct {
	store      store.Store
	slackToken string
	playbooks  map[string]*deployment.Playbook
	manifests  map[string]*deployment.Manifest
	deployer   deployment.Deployer
	engine     *gin.Engine
}

const commandHint string = `/broadway help: This message
/broadway deploy myPlaybookID myInstanceID: Deploy a new instance`

// ErrorResponse represents a JSON response to be returned in failure cases
type ErrorResponse map[string]string

var (
	// BadRequestError represents a JSON response for status 400
	BadRequestError = ErrorResponse{"error": "Bad Request"}
	// UnauthorizedError represents a JSON response for status 401
	UnauthorizedError = ErrorResponse{"error": "Unauthorized"}
	// NotFoundError represents a JSON response for status 404
	NotFoundError = ErrorResponse{"error": "Not Found"}
	// InternalError represents a JSON response for status 500
	InternalError = ErrorResponse{"error": "Internal Server Error"}
)

// CustomError creates an ErrorResponse with a custom message
func CustomError(message string) ErrorResponse {
	return ErrorResponse{"error": message}
}

// New instantiates a new Server and binds its handlers. The Server will look
// for playbooks and instances in store `s`
func New(s store.Store) *Server {
	srvr := &Server{
		store:      s,
		slackToken: env.SlackToken,
	}
	srvr.setupHandlers()
	return srvr
}

// Init initializes manifests and playbooks for the server.
func (s *Server) Init() {
	ms := services.NewManifestService(env.ManifestsPath)

	var err error
	s.manifests, err = ms.LoadManifestFolder()
	if err != nil {
		glog.Fatal(err)
	}

	s.playbooks = deployment.AllPlaybooks
	glog.Infof("Server Playbooks: %+v", s.playbooks)
}

func (s *Server) setupHandlers() {
	s.engine = gin.Default()
	gin.SetMode(gin.ReleaseMode) // Comment this to use debug mode for more verbose output
	// Define routes:
	s.engine.POST("/command", s.postCommand)
	s.engine.GET("/command", s.getCommand)
	// Protect subsequent routes with middleware:
	s.engine.Use(authMiddleware)
	s.engine.GET("/", s.home)
	s.engine.POST("/instances", s.createInstance)
	s.engine.GET("/instance/:playbookID/:instanceID", s.getInstance)
	s.engine.GET("/instances/:playbookID", s.getInstances)
	s.engine.GET("/status/:playbookID/:instanceID", s.getStatus)
	s.engine.POST("/deploy/:playbookID/:instanceID", s.deployInstance)
	s.engine.DELETE("/instances/:playbookID/:instanceID", s.deleteInstance)
}

// Handler returns a reference to the Gin engine that powers Server
func (s *Server) Handler() http.Handler {
	return s.engine
}

// Run starts the server on the specified address
func (s *Server) Run(addr ...string) error {
	return s.engine.Run(addr...)
}

func authMiddleware(c *gin.Context) {
	a := c.Request.Header.Get("Authorization")
	a = strings.TrimPrefix(a, "Bearer ")
	if len(a) == 0 || a != env.AuthBearerToken {
		glog.Infof("Auth failure for %s\nExpected: %s Actual: %s\n", c.Request.URL.Path, env.AuthBearerToken, a)
		c.String(http.StatusUnauthorized, "Wrong or Missing Authorization")
		c.AbortWithStatus(http.StatusUnauthorized)
		return
	}
	c.Next()
}

func (s *Server) home(c *gin.Context) {
	c.String(http.StatusOK, "Welcome to Broadway!")
}

func (s *Server) createInstance(c *gin.Context) {
	var i instance.Instance
	if err := c.BindJSON(&i); err != nil {
		glog.Error(err)
		c.JSON(http.StatusBadRequest, CustomError("Missing: "+err.Error()))
		return
	}

	service := services.NewInstanceService(store.New())
	createdInstance, err := service.Create(&i)

	if err != nil {
		glog.Error(err)
		c.JSON(http.StatusInternalServerError, InternalError)
		return
	}

	c.JSON(http.StatusCreated, createdInstance)
}

func (s *Server) getInstance(c *gin.Context) {
	service := services.NewInstanceService(s.store)
	i, err := service.Show(c.Param("playbookID"), c.Param("instanceID"))

	if err != nil {
		switch err.(type) {
		case instance.NotFound:
			c.JSON(http.StatusNotFound, NotFoundError)
			return
		default:
			c.JSON(http.StatusInternalServerError, InternalError)
			return
		}
	}
	c.JSON(http.StatusOK, i)
}

func (s *Server) getInstances(c *gin.Context) {
	service := services.NewInstanceService(s.store)
	instances, err := service.AllWithPlaybookID(c.Param("playbookID"))
	if err != nil {
		c.JSON(http.StatusInternalServerError, InternalError)
		return
	}
	c.JSON(http.StatusOK, instances)
	return
}

func (s *Server) getStatus(c *gin.Context) {
	service := services.NewInstanceService(s.store)
	i, err := service.Show(c.Param("playbookID"), c.Param("instanceID"))

	if err != nil {
		switch err.(type) {
		case instance.NotFound:
			c.JSON(http.StatusNotFound, NotFoundError)
			return
		default:
			c.JSON(http.StatusInternalServerError, InternalError)
			return
		}
	}
	c.JSON(http.StatusOK, map[string]string{
		"status": string(i.Status),
	})
}

func (s *Server) getCommand(c *gin.Context) {
	ssl := c.Query("ssl_check")
	glog.Info(ssl)
	if ssl == "1" {
		c.String(http.StatusOK, "")
	} else {
		c.String(http.StatusBadRequest, "Use POST /command")
	}
}

// SlackCommand represents the unmarshalled JSON post data from Slack
type SlackCommand struct {
	Token       string `form:"token"`
	TeamID      string `form:"team_id"`
	TeamDomain  string `form:"team_domain"`
	ChannelID   string `form:"channel_id"`
	ChannelName string `form:"channel_name"`
	UserID      string `form:"user_id"`
	UserName    string `form:"user_name"`
	Command     string `form:"command"`
	Text        string `form:"text"`
	ResponseURL string `form:"response_url"`
}

func (s *Server) postCommand(c *gin.Context) {
	var form SlackCommand
	if err := c.BindWith(&form, binding.Form); err != nil {
		glog.Error(err)
		c.JSON(http.StatusInternalServerError, InternalError)
		return
	}

	if form.Token != s.slackToken {
		glog.Errorf("Token mismatch, actual: %s, expected: %s\n", form.Token, s.slackToken)
		c.JSON(http.StatusUnauthorized, UnauthorizedError)
		return
	}

	is := services.NewInstanceService(s.store)
	slackCommand := services.BuildSlackCommand(form.Text, is, s.playbooks)
	glog.Infof("Running command: %s", form.Text)
	msg, err := slackCommand.Execute()
	if err != nil {
		c.JSON(http.StatusOK, err)
		return
	}

	// Craft a Slack payload for an ephemeral message:
	j := notification.NewMessage(false, msg)
	c.JSON(http.StatusOK, j)
	return
}

func deploy(s *Server, pID string, ID string) (*instance.Instance, error) {
	is := services.NewInstanceService(s.store)
	i, err := is.Show(pID, ID)
	if err != nil {
		return nil, err
	}

	ds := services.NewDeploymentService(s.store, s.playbooks, s.manifests)

	err = ds.DeployAndNotify(i)
	if err != nil {
		return nil, err
	}
	return i, nil
}

func del(s *Server, pID string, ID string) (*instance.Instance, error) {
	is := services.NewInstanceService(s.store)
	i, err := is.Show(pID, ID)
	if err != nil {
		return nil, err
	}

	ds := services.NewDeploymentService(s.store, s.playbooks, s.manifests)

	err = ds.DeleteAndNotify(i)
	if err != nil {
		return nil, err
	}
	return i, nil
}

func (s *Server) deployInstance(c *gin.Context) {
	i, err := deploy(s, c.Param("playbookID"), c.Param("instanceID"))
	if err != nil {
		glog.Error(err)
		switch err.(type) {
		case instance.NotFound:
			c.JSON(http.StatusNotFound, NotFoundError)
			return
		default:
			c.JSON(http.StatusInternalServerError, InternalError)
			return
		}
	}
	c.JSON(http.StatusOK, i)
}

func (s *Server) deleteInstance(c *gin.Context) {
	_, err := del(s, c.Param("playbookID"), c.Param("instanceID"))
	if err != nil {
		glog.Error(err)
		switch err.(type) {
		case instance.NotFound:
			c.JSON(http.StatusNotFound, NotFoundError)
			return
		default:
			c.JSON(http.StatusInternalServerError, InternalError)
			return
		}
	}
	c.JSON(http.StatusOK, map[string]string{"message": "Instance successfully deleted"})
}
<|endoftext|>


<commit_before>package settings

import "runtime"

// Soft settings are some configurations than can be safely changed and
// the app need to be restarted to apply such configuration changes.
var Soft = getSoftSettings()

type soft struct {
	// test
	TestInt  uint64 `json:"test_int,omitempty"`
	TestBool bool   `json:"test_bool"`
	TestStr  string `json:"test_str"`
	// raft
	MaxCommittedSizePerReady uint64 `json:"max_committed_size_per_ready"`
	MaxSizePerMsg            uint64 `json:"max_size_per_msg"`
	MaxInflightMsgs          uint64 `json:"max_inflight_msgs"`
	DefaultSnapCount         uint64 `json:"default_snap_count"`
	MaxInFlightMsgSnap       uint64 `json:"max_in_flight_msg_snap"`
	// HealthInterval is the minimum time the cluster should be healthy
	// before accepting add member requests.
	HealthIntervalSec uint64 `json:"health_interval_sec"`

	// transport

	// statemachine
	CommitBufferLen uint64 `json:"commit_buffer_len"`

	// server

	// raft proposal queue length for client queue loop (1024*4 for default, suggest use default)
	ProposalQueueLen uint64 `json:"proposal_queue_len"`
	// how many queues used for proposal, suggest use CPU nums
	ProposalQueueNum uint64 `json:"proposal_queue_num"`
}

func getSoftSettings() soft {
	d := defaultSoftSettings()
	overwriteSettingsWithFile(&d, "soft-settings.json")
	return d
}

func defaultSoftSettings() soft {
	return soft{
		MaxCommittedSizePerReady: 1024 * 1024 * 16,
		DefaultSnapCount:         160000,
		HealthIntervalSec:        5,
		// max number of in-flight snapshot messages allows to have
		MaxInFlightMsgSnap: 16,
		MaxSizePerMsg:      1024 * 1024,
		MaxInflightMsgs:    256,
		CommitBufferLen:    5000,
		ProposalQueueLen:   1024 * 4,
		ProposalQueueNum:   uint64(runtime.NumCPU()),
	}
}
<commit_msg>reduce max allowed message size for raft<commit_after>package settings

import "runtime"

// Soft settings are some configurations than can be safely changed and
// the app need to be restarted to apply such configuration changes.
var Soft = getSoftSettings()

type soft struct {
	// test
	TestInt  uint64 `json:"test_int,omitempty"`
	TestBool bool   `json:"test_bool"`
	TestStr  string `json:"test_str"`
	// raft
	MaxCommittedSizePerReady uint64 `json:"max_committed_size_per_ready"`
	MaxSizePerMsg            uint64 `json:"max_size_per_msg"`
	MaxInflightMsgs          uint64 `json:"max_inflight_msgs"`
	DefaultSnapCount         uint64 `json:"default_snap_count"`
	MaxInFlightMsgSnap       uint64 `json:"max_in_flight_msg_snap"`
	// HealthInterval is the minimum time the cluster should be healthy
	// before accepting add member requests.
	HealthIntervalSec uint64 `json:"health_interval_sec"`

	// transport

	// statemachine
	CommitBufferLen uint64 `json:"commit_buffer_len"`

	// server

	// raft proposal queue length for client queue loop (1024*4 for default, suggest use default)
	ProposalQueueLen uint64 `json:"proposal_queue_len"`
	// how many queues used for proposal, suggest use CPU nums
	ProposalQueueNum uint64 `json:"proposal_queue_num"`
}

func getSoftSettings() soft {
	d := defaultSoftSettings()
	overwriteSettingsWithFile(&d, "soft-settings.json")
	return d
}

func defaultSoftSettings() soft {
	return soft{
		MaxCommittedSizePerReady: 1024 * 1024 * 16,
		DefaultSnapCount:         160000,
		HealthIntervalSec:        5,
		// max number of in-flight snapshot messages allows to have
		MaxInFlightMsgSnap: 16,
		MaxSizePerMsg:      512 * 1024,
		MaxInflightMsgs:    256,
		CommitBufferLen:    5000,
		ProposalQueueLen:   1024 * 4,
		ProposalQueueNum:   uint64(runtime.NumCPU()),
	}
}
<|endoftext|>


<commit_before>package main_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

func TestRatelimitService(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Experiment Suite")
}
<commit_msg>Gave test suite correct name<commit_after>package main_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

func TestExperimentService(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Experiment Suite")
}
<|endoftext|>


<commit_before>// Copyright 2015 Apcera Inc. All rights reserved.

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"strings"
	"sync"
	"time"

	"github.com/nats-io/nats"
	"github.com/nats-io/nats/bench"
)

// Some sane defaults
const (
	DefaultNumMsgs     = 100000
	DefaultNumPubs     = 1
	DefaultNumSubs     = 0
	DefaultMessageSize = 128
)

func usage() {
	log.Fatalf("Usage: nats-bench [-s server (%s)] [--tls] [-np NUM_PUBLISHERS] [-ns NUM_SUBSCRIBERS] [-n NUM_MSGS] [-ms MESSAGE_SIZE] [-csv csvfile] <subject>\n", nats.DefaultURL)
}

var benchmark *bench.Benchmark

func main() {
	var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)")
	var tls = flag.Bool("tls", false, "Use TLS Secure Connection")
	var numPubs = flag.Int("np", DefaultNumPubs, "Number of Concurrent Publishers")
	var numSubs = flag.Int("ns", DefaultNumSubs, "Number of Concurrent Subscribers")
	var numMsgs = flag.Int("n", DefaultNumMsgs, "Number of Messages to Publish")
	var msgSize = flag.Int("ms", DefaultMessageSize, "Size of the message.")
	var csvFile = flag.String("csv", "", "Save bench data to csv file")

	log.SetFlags(0)
	flag.Usage = usage
	flag.Parse()

	args := flag.Args()
	if len(args) != 1 {
		usage()
	}

	// Setup the option block
	opts := nats.DefaultOptions
	opts.Servers = strings.Split(*urls, ",")
	for i, s := range opts.Servers {
		opts.Servers[i] = strings.Trim(s, " ")
	}
	opts.Secure = *tls

	benchmark = bench.NewBenchmark("NATS", *numSubs, *numPubs)

	var startwg sync.WaitGroup
	var donewg sync.WaitGroup

	donewg.Add(*numPubs + *numSubs)

	// Run Subscribers first
	startwg.Add(*numSubs)
	for i := 0; i < *numSubs; i++ {
		go runSubscriber(&startwg, &donewg, opts, *numMsgs, *msgSize)
	}
	startwg.Wait()

	// Now Publishers
	startwg.Add(*numPubs)
	pubCounts := bench.MsgsPerClient(*numMsgs, *numPubs)
	for i := 0; i < *numPubs; i++ {
		go runPublisher(&startwg, &donewg, opts, pubCounts[i], *msgSize)
	}

	log.Printf("Starting benchmark [msgs=%d, msgsize=%d, pubs=%d, subs=%d]\n", *numMsgs, *msgSize, *numPubs, *numSubs)

	startwg.Wait()
	donewg.Wait()

	benchmark.Close()

	fmt.Print(benchmark.Report())

	if len(*csvFile) > 0 {
		csv := benchmark.CSV()
		ioutil.WriteFile(*csvFile, []byte(csv), 0644)
		fmt.Printf("Saved metric data in csv file %s\n", *csvFile)
	}
}

func runPublisher(startwg, donewg *sync.WaitGroup, opts nats.Options, numMsgs int, msgSize int) {
	nc, err := opts.Connect()
	if err != nil {
		log.Fatalf("Can't connect: %v\n", err)
	}
	defer nc.Close()
	startwg.Done()

	args := flag.Args()
	subj := args[0]
	var msg []byte
	if msgSize > 0 {
		msg = make([]byte, msgSize)
	}

	start := time.Now()

	for i := 0; i < numMsgs; i++ {
		nc.Publish(subj, msg)
	}
	nc.Flush()
	benchmark.AddPubSample(bench.NewSample(numMsgs, msgSize, start, time.Now(), nc))

	donewg.Done()
}

func runSubscriber(startwg, donewg *sync.WaitGroup, opts nats.Options, numMsgs int, msgSize int) {
	nc, err := opts.Connect()
	if err != nil {
		log.Fatalf("Can't connect: %v\n", err)
	}

	args := flag.Args()
	subj := args[0]

	received := 0
	start := time.Now()
	nc.Subscribe(subj, func(msg *nats.Msg) {
		received++
		if received >= numMsgs {
			benchmark.AddSubSample(bench.NewSample(numMsgs, msgSize, start, time.Now(), nc))
			donewg.Done()
			nc.Close()
		}
	})
	nc.Flush()
	startwg.Done()
}
<commit_msg>[FIX #227] Added check to insure message count is greater than zero<commit_after>// Copyright 2015 Apcera Inc. All rights reserved.

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"strings"
	"sync"
	"time"

	"github.com/nats-io/nats"
	"github.com/nats-io/nats/bench"
)

// Some sane defaults
const (
	DefaultNumMsgs     = 100000
	DefaultNumPubs     = 1
	DefaultNumSubs     = 0
	DefaultMessageSize = 128
)

func usage() {
	log.Fatalf("Usage: nats-bench [-s server (%s)] [--tls] [-np NUM_PUBLISHERS] [-ns NUM_SUBSCRIBERS] [-n NUM_MSGS] [-ms MESSAGE_SIZE] [-csv csvfile] <subject>\n", nats.DefaultURL)
}

var benchmark *bench.Benchmark

func main() {
	var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)")
	var tls = flag.Bool("tls", false, "Use TLS Secure Connection")
	var numPubs = flag.Int("np", DefaultNumPubs, "Number of Concurrent Publishers")
	var numSubs = flag.Int("ns", DefaultNumSubs, "Number of Concurrent Subscribers")
	var numMsgs = flag.Int("n", DefaultNumMsgs, "Number of Messages to Publish")
	var msgSize = flag.Int("ms", DefaultMessageSize, "Size of the message.")
	var csvFile = flag.String("csv", "", "Save bench data to csv file")

	log.SetFlags(0)
	flag.Usage = usage
	flag.Parse()

	args := flag.Args()
	if len(args) != 1 {
		usage()
	}

	if *numMsgs < 1 {
		log.Fatal("Number of messages should be greater than zero.")
	}

	// Setup the option block
	opts := nats.DefaultOptions
	opts.Servers = strings.Split(*urls, ",")
	for i, s := range opts.Servers {
		opts.Servers[i] = strings.Trim(s, " ")
	}
	opts.Secure = *tls

	benchmark = bench.NewBenchmark("NATS", *numSubs, *numPubs)

	var startwg sync.WaitGroup
	var donewg sync.WaitGroup

	donewg.Add(*numPubs + *numSubs)

	// Run Subscribers first
	startwg.Add(*numSubs)
	for i := 0; i < *numSubs; i++ {
		go runSubscriber(&startwg, &donewg, opts, *numMsgs, *msgSize)
	}
	startwg.Wait()

	// Now Publishers
	startwg.Add(*numPubs)
	pubCounts := bench.MsgsPerClient(*numMsgs, *numPubs)
	for i := 0; i < *numPubs; i++ {
		go runPublisher(&startwg, &donewg, opts, pubCounts[i], *msgSize)
	}

	log.Printf("Starting benchmark [msgs=%d, msgsize=%d, pubs=%d, subs=%d]\n", *numMsgs, *msgSize, *numPubs, *numSubs)

	startwg.Wait()
	donewg.Wait()

	benchmark.Close()

	fmt.Print(benchmark.Report())

	if len(*csvFile) > 0 {
		csv := benchmark.CSV()
		ioutil.WriteFile(*csvFile, []byte(csv), 0644)
		fmt.Printf("Saved metric data in csv file %s\n", *csvFile)
	}
}

func runPublisher(startwg, donewg *sync.WaitGroup, opts nats.Options, numMsgs int, msgSize int) {
	nc, err := opts.Connect()
	if err != nil {
		log.Fatalf("Can't connect: %v\n", err)
	}
	defer nc.Close()
	startwg.Done()

	args := flag.Args()
	subj := args[0]
	var msg []byte
	if msgSize > 0 {
		msg = make([]byte, msgSize)
	}

	start := time.Now()

	for i := 0; i < numMsgs; i++ {
		nc.Publish(subj, msg)
	}
	nc.Flush()
	benchmark.AddPubSample(bench.NewSample(numMsgs, msgSize, start, time.Now(), nc))

	donewg.Done()
}

func runSubscriber(startwg, donewg *sync.WaitGroup, opts nats.Options, numMsgs int, msgSize int) {
	nc, err := opts.Connect()
	if err != nil {
		log.Fatalf("Can't connect: %v\n", err)
	}

	args := flag.Args()
	subj := args[0]

	received := 0
	start := time.Now()
	nc.Subscribe(subj, func(msg *nats.Msg) {
		received++
		if received >= numMsgs {
			benchmark.AddSubSample(bench.NewSample(numMsgs, msgSize, start, time.Now(), nc))
			donewg.Done()
			nc.Close()
		}
	})
	nc.Flush()
	startwg.Done()
}
<|endoftext|>


<commit_before>package shell

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"syscall"
)

type Process struct {
	// The command to run in the process
	Command *Command

	// Additonal config for the process
	Config *Config

	// The exit status of the process
	exitStatus int
}

func (p *Process) Run() error {
	var err error

	cmd := exec.Command(p.Command.Command, p.Command.Args...)

	if p.Command.Env != nil {
		cmd.Env = p.Command.Env.ToSlice()
	}

	if p.Command.Dir != "" {
		cmd.Dir = p.Command.Dir
	}

	if p.Config.PTY {
		// Start our process in a PTY
		pty, err := ptyStart(cmd)
		if err != nil {
			return fmt.Errorf("Failed to start PTY: ", err)
		}

		// Copy the pty to our buffer. This will block until it EOF's
		// or something breaks.
		_, err = io.Copy(p.Config.Writer, pty)
		if e, ok := err.(*os.PathError); ok && e.Err == syscall.EIO {
			// We can safely ignore this error, because it's just
			// the PTY telling us that it closed successfully.
			// See:
			// https://github.com/buildkite/agent/pull/34#issuecomment-46080419
			err = nil
		}
	} else {
		cmd.Stdout = p.Config.Writer
		cmd.Stderr = p.Config.Writer

		err := cmd.Start()
		if err != nil {
			return fmt.Errorf("Failed to start command: ", err)
		}
	}

	// Wait for the command to finish
	waitResult := cmd.Wait()

	// Get the exit status
	p.exitStatus, err = getExitStatusFromWaitResult(waitResult)
	if err != nil {
		return fmt.Errorf("Failed to get exit status: %s", err)
	}

	return nil
}

func (p *Process) ExitStatus() int {
	return p.exitStatus
}

// https://github.com/hnakamur/commango/blob/fe42b1cf82bf536ce7e24dceaef6656002e03743/os/executil/executil.go#L29
// TODO: Can this be better?
func getExitStatusFromWaitResult(waitResult error) (int, error) {
	exitStatus := -1

	if waitResult != nil {
		if err, ok := waitResult.(*exec.ExitError); ok {
			if s, ok := err.Sys().(syscall.WaitStatus); ok {
				exitStatus = s.ExitStatus()
			} else {
				return -1, errors.New("Unimplemented for system where exec.ExitError.Sys() is not syscall.WaitStatus.")
			}
		}
	} else {
		exitStatus = 0
	}

	return exitStatus, nil
}
<commit_msg>Cleaned up exit status handling in shell/process.go<commit_after>package shell

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"syscall"
)

type Process struct {
	// The command to run in the process
	Command *Command

	// Additonal config for the process
	Config *Config

	// The exit status of the process
	exitStatus int
}

func (p *Process) Run() error {
	cmd := exec.Command(p.Command.Command, p.Command.Args...)

	if p.Command.Env != nil {
		cmd.Env = p.Command.Env.ToSlice()
	}

	if p.Command.Dir != "" {
		cmd.Dir = p.Command.Dir
	}

	if p.Config.PTY {
		// Start our process in a PTY
		pty, err := ptyStart(cmd)
		if err != nil {
			return fmt.Errorf("Failed to start PTY: ", err)
		}

		// Copy the pty to our buffer. This will block until it EOF's
		// or something breaks.
		_, err = io.Copy(p.Config.Writer, pty)
		if e, ok := err.(*os.PathError); ok && e.Err == syscall.EIO {
			// We can safely ignore this error, because it's just
			// the PTY telling us that it closed successfully.
			// See:
			// https://github.com/buildkite/agent/pull/34#issuecomment-46080419
			err = nil
		}
	} else {
		cmd.Stdout = p.Config.Writer
		cmd.Stderr = p.Config.Writer

		err := cmd.Start()
		if err != nil {
			return fmt.Errorf("Failed to start command: ", err)
		}
	}

	// Wait for the command to finish
	waitResult := cmd.Wait()

	// Get the exit status
	// https://github.com/hnakamur/commango/blob/fe42b1cf82bf536ce7e24dceaef6656002e03743/os/executil/executil.go#L29
	if waitResult != nil {
		if err, ok := waitResult.(*exec.ExitError); ok {
			if s, ok := err.Sys().(syscall.WaitStatus); ok {
				p.exitStatus = s.ExitStatus()
			} else {
				return errors.New("Unimplemented for system where exec.ExitError.Sys() is not syscall.WaitStatus.")
			}
		}
	} else {
		p.exitStatus = 0
	}

	return nil
}

func (p *Process) ExitStatus() int {
	return p.exitStatus
}
<|endoftext|>


<commit_before>package hdfs

import (
	"errors"
	//"fmt"
	"io/ioutil"
	//"log"
	"github.com/eaciit/colony-core/v0"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
)

func (h *WebHdfs) GetToLocal(path string, destination string, permission string) error {
	d, err := h.Get(path)
	if err != nil {
		return err
	}
	if permission == "" {
		permission = "755"
	}
	iperm, _ := strconv.Atoi(permission)
	err = ioutil.WriteFile(destination, d, os.FileMode(iperm))
	if err != nil {
		return err
	}
	return nil
}

func (h *WebHdfs) Get(path string) ([]byte, error) {
	r, err := h.call("GET", path, OP_OPEN, nil)
	if err != nil {
		return nil, err
	}
	if r.StatusCode != 307 {
		return nil, errors.New("Invalid Response Header on OP_OPEN: " + r.Status)
	}

	location := r.Header["Location"][0]
	r, err = h.call("GET", location, OP_OPEN, nil)
	if err != nil {
		return nil, err
	}
	if r.StatusCode != 200 {
		return nil, errors.New(r.Status)
	}
	d, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	return d, nil
}

func mergeMapString(source map[string]string, adds map[string]string) map[string]string {
	if source == nil {
		source = make(map[string]string)
	}
	if adds != nil {
		for k, v := range adds {
			source[k] = v
		}
	}
	return source
}

func (h *WebHdfs) Put(localfile string, destination string, permission string, parms map[string]string, server *colonycore.Server) error {
	if permission == "" {
		permission = "755"
	}
	parms = mergeMapString(parms, map[string]string{"permission": permission})
	r, err := h.call("PUT", destination, OP_CREATE, parms)
	if err != nil {
		return err
	}
	if r.StatusCode != 307 {
		return errors.New("Invalid Response Header on OP_CREATE: " + r.Status)
	}

	location := r.Header["Location"][0]
	if server != nil {
		for _, alias := range server.HostAlias {
			if strings.Contains(strings.Split(location, ":")[1], alias.HostName) {
				location = strings.Replace(location, alias.HostName, alias.IP, 1)
				break
			}
		}
	}

	r, err = h.callPayload("PUT", location, OP_CREATE, localfile, nil)
	if err != nil {
		return err
	}
	if r.StatusCode != 201 {
		return errors.New(r.Status)
	}
	return nil
}

func (h *WebHdfs) Puts(paths []string, destinationFolder string, permission string, parms map[string]string) map[string]error {
	var es map[string]error
	if permission == "" {
		permission = "755"
	}

	fileCount := len(paths)

	//parms = mergeMapString(parms, map[string]string{"permission": strconv.Itoa(permission)})
	ipool := 0
	iprocessing := 0
	iread := 0
	files := []string{}
	for _, path := range paths {
		ipool = ipool + 1
		iread = iread + 1
		files = append(files, path)
		if ipool == h.Config.PoolSize || iread == fileCount {
			wg := sync.WaitGroup{}
			wg.Add(ipool)

			for _, f := range files {
				go func(path string, swg *sync.WaitGroup) {
					defer swg.Done()
					iprocessing = iprocessing + 1
					_, filename := filepath.Split(path)
					newfilename := filepath.Join(destinationFolder, filename)
					e := h.Put(path, newfilename, permission, parms)
					//var e error
					if e != nil {
						if es == nil {
							es = make(map[string]error)
							es[path] = e
						}
						//fmt.Println(path, "=> ", newfilename, " ... FAIL => ", e.Error(), " | Processing ", iprocessing, " of ", fileCount)
					} else {
						//fmt.Println(path, "=> ", newfilename, " ... SUCCESS | Processing ", iprocessing, " of ", fileCount)
					}
				}(f, &wg)
			}

			wg.Wait()
			ipool = 0
			files = []string{}
		}
	}

	return es
}

func (h *WebHdfs) Append(localfile string, destination string) error {
	r, err := h.call("POST", destination, OP_APPEND, nil)
	if err != nil {
		return err
	}
	if r.StatusCode != 307 {
		return errors.New("Invalid Response Header on OP_APPEND: " + r.Status)
	}

	location := r.Header["Location"][0]

	r, err = h.callPayload("POST", location, OP_APPEND, localfile, nil)
	if err != nil {
		return err
	}
	if r.StatusCode != 201 {
		return errors.New(r.Status)
	}
	return nil
}

func (h *WebHdfs) SetOwner(path string, owner string, group string) error {
	ownerInfo := map[string]string{}
	if owner != "" {
		ownerInfo["owner"] = owner
	}
	if group != "" {
		ownerInfo["group"] = group
	}
	r, e := h.call("PUT", path, OP_SETOWNER, ownerInfo)
	if e != nil {
		return e
	}
	if r.StatusCode != 200 {
		return errors.New("Invalid Response Header on OP_SETOWNER: " + r.Status)
	}
	return nil
}

func (h *WebHdfs) SetPermission(path string, permission string) error {
	if permission == "" {
		permission = "755"
	}

	parms := map[string]string{}
	parms["permission"] = permission

	r, e := h.call("PUT", path, OP_SETPERMISSION, parms)
	if e != nil {
		return e
	}
	if r.StatusCode != 200 {
		return errors.New("Invalid Response Header on OP_SETPERMISSION: " + r.Status)
	}
	return nil
}

/*
func (h *WebHdfs) CreateNewFile(path, filename, permission string) error {
	if permission == "" {
		permission = "755"
	}

	parms := map[string]string{}
	parms["permission"] = permission

	var fullpath string

	if string(path[len(path)-1]) == "/" {
		fullpath = path + filename
	} else {
		fullpath = path + "/" + filename
	}

	log.Println(fullpath)

	r, e := h.call("PUT", fullpath, OP_CREATE, parms)
	if e != nil {
		return e
	}
	if r.StatusCode != 200 {
		return errors.New("Invalid Response Header on OP_CREATE: " + r.Status)
	}
	return nil
}
*/
<commit_msg>update Get<commit_after>package hdfs

import (
	"errors"
	//"fmt"
	"io/ioutil"
	//"log"
	"github.com/eaciit/colony-core/v0"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
)

func (h *WebHdfs) GetToLocal(path string, destination string, permission string, server *colonycore.Server) error {
	d, err := h.Get(path, server)
	if err != nil {
		return err
	}
	if permission == "" {
		permission = "755"
	}
	iperm, _ := strconv.Atoi(permission)
	err = ioutil.WriteFile(destination, d, os.FileMode(iperm))
	if err != nil {
		return err
	}
	return nil
}

func (h *WebHdfs) Get(path string, server *colonycore.Server) ([]byte, error) {
	r, err := h.call("GET", path, OP_OPEN, nil)
	isRedirected := false
	if err != nil {
		if strings.Contains(strings.ToLower(err.Error()), "no such host is known") {
			isRedirected = true
		} else {
			return nil, err
		}
	}
	if r != nil {
		if r.StatusCode != 307 {
			return nil, errors.New("Invalid Response Header on OP_OPEN: " + r.Status)
		}
	}

	location := ""
	if isRedirected {
		location = "http:" + strings.Split(err.Error(), ":")[1] + ":" + strings.Split(err.Error(), ":")[2] + ":" + strings.Split(err.Error(), ":")[3]
	} else {
		location = r.Header["Location"][0]
	}

	if server != nil {
		for _, alias := range server.HostAlias {
			if strings.Contains(strings.Split(location, ":")[1], alias.HostName) {
				location = strings.Replace(location, alias.HostName, alias.IP, 1)
				break
			}
		}
	}

	r, err = h.call("GET", location, OP_OPEN, nil)
	if err != nil {
		return nil, err
	}
	if r.StatusCode != 200 {
		return nil, errors.New(r.Status)
	}
	d, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	return d, nil
}

func mergeMapString(source map[string]string, adds map[string]string) map[string]string {
	if source == nil {
		source = make(map[string]string)
	}
	if adds != nil {
		for k, v := range adds {
			source[k] = v
		}
	}
	return source
}

func (h *WebHdfs) Put(localfile string, destination string, permission string, parms map[string]string, server *colonycore.Server) error {
	if permission == "" {
		permission = "755"
	}
	parms = mergeMapString(parms, map[string]string{"permission": permission})
	r, err := h.call("PUT", destination, OP_CREATE, parms)
	if err != nil {
		return err
	}
	if r.StatusCode != 307 {
		return errors.New("Invalid Response Header on OP_CREATE: " + r.Status)
	}

	location := r.Header["Location"][0]
	if server != nil {
		for _, alias := range server.HostAlias {
			if strings.Contains(strings.Split(location, ":")[1], alias.HostName) {
				location = strings.Replace(location, alias.HostName, alias.IP, 1)
				break
			}
		}
	}
	r, err = h.callPayload("PUT", location, OP_CREATE, localfile, nil)
	if err != nil {
		return err
	}
	if r.StatusCode != 201 {
		return errors.New(r.Status)
	}
	return nil
}

func (h *WebHdfs) Puts(paths []string, destinationFolder string, permission string, parms map[string]string, server *colonycore.Server) map[string]error {
	var es map[string]error
	if permission == "" {
		permission = "755"
	}

	fileCount := len(paths)

	//parms = mergeMapString(parms, map[string]string{"permission": strconv.Itoa(permission)})
	ipool := 0
	iprocessing := 0
	iread := 0
	files := []string{}
	for _, path := range paths {
		ipool = ipool + 1
		iread = iread + 1
		files = append(files, path)
		if ipool == h.Config.PoolSize || iread == fileCount {
			wg := sync.WaitGroup{}
			wg.Add(ipool)

			for _, f := range files {
				go func(path string, swg *sync.WaitGroup) {
					defer swg.Done()
					iprocessing = iprocessing + 1
					_, filename := filepath.Split(path)
					newfilename := filepath.Join(destinationFolder, filename)
					e := h.Put(path, newfilename, permission, parms, server)
					//var e error
					if e != nil {
						if es == nil {
							es = make(map[string]error)
							es[path] = e
						}
						//fmt.Println(path, "=> ", newfilename, " ... FAIL => ", e.Error(), " | Processing ", iprocessing, " of ", fileCount)
					} else {
						//fmt.Println(path, "=> ", newfilename, " ... SUCCESS | Processing ", iprocessing, " of ", fileCount)
					}
				}(f, &wg)
			}

			wg.Wait()
			ipool = 0
			files = []string{}
		}
	}

	return es
}

func (h *WebHdfs) Append(localfile string, destination string) error {
	r, err := h.call("POST", destination, OP_APPEND, nil)
	if err != nil {
		return err
	}
	if r.StatusCode != 307 {
		return errors.New("Invalid Response Header on OP_APPEND: " + r.Status)
	}

	location := r.Header["Location"][0]

	r, err = h.callPayload("POST", location, OP_APPEND, localfile, nil)
	if err != nil {
		return err
	}
	if r.StatusCode != 201 {
		return errors.New(r.Status)
	}
	return nil
}

func (h *WebHdfs) SetOwner(path string, owner string, group string) error {
	ownerInfo := map[string]string{}
	if owner != "" {
		ownerInfo["owner"] = owner
	}
	if group != "" {
		ownerInfo["group"] = group
	}
	r, e := h.call("PUT", path, OP_SETOWNER, ownerInfo)
	if e != nil {
		return e
	}
	if r.StatusCode != 200 {
		return errors.New("Invalid Response Header on OP_SETOWNER: " + r.Status)
	}
	return nil
}

func (h *WebHdfs) SetPermission(path string, permission string) error {
	if permission == "" {
		permission = "755"
	}

	parms := map[string]string{}
	parms["permission"] = permission

	r, e := h.call("PUT", path, OP_SETPERMISSION, parms)
	if e != nil {
		return e
	}
	if r.StatusCode != 200 {
		return errors.New("Invalid Response Header on OP_SETPERMISSION: " + r.Status)
	}
	return nil
}

/*
func (h *WebHdfs) CreateNewFile(path, filename, permission string) error {
	if permission == "" {
		permission = "755"
	}

	parms := map[string]string{}
	parms["permission"] = permission

	var fullpath string

	if string(path[len(path)-1]) == "/" {
		fullpath = path + filename
	} else {
		fullpath = path + "/" + filename
	}

	log.Println(fullpath)

	r, e := h.call("PUT", fullpath, OP_CREATE, parms)
	if e != nil {
		return e
	}
	if r.StatusCode != 200 {
		return errors.New("Invalid Response Header on OP_CREATE: " + r.Status)
	}
	return nil
}
*/
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
)

func main() {
	nLines := flag.Int("n", 10, "number of lines")
	quiet := flag.Bool("q", false, "print file headers")
	flag.Parse()

	var numfiles int = len(flag.Args())

	for i, file := range flag.Args() {
		f, err := os.Open(file)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(-1)
		}

		var last_line bool = false
		var lines int = 0

		if !*quiet && numfiles > 1 {
			fmt.Printf("==> %s <==\n", file)
		}

		nr := bufio.NewReader(f)
		for {
			line, err := nr.ReadString('\n')
			if err == io.EOF {
				last_line = true
			} else if err != nil {
				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			}

			lines++

			fmt.Print(line)

			if *nLines == lines || last_line {
				break
			}

		}

		/* Print multi file seperator */
		if !*quiet && numfiles > 1 && i < len(flag.Args())-1 {
			fmt.Println("")
		}
	}
}
<commit_msg>Correct -q option description for head<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
)

func main() {
	nLines := flag.Int("n", 10, "number of lines")
	quiet := flag.Bool("q", false, "supress file headers")
	flag.Parse()

	var numfiles int = len(flag.Args())

	for i, file := range flag.Args() {
		f, err := os.Open(file)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(-1)
		}

		var last_line bool = false
		var lines int = 0

		if !*quiet && numfiles > 1 {
			fmt.Printf("==> %s <==\n", file)
		}

		nr := bufio.NewReader(f)
		for {
			line, err := nr.ReadString('\n')
			if err == io.EOF {
				last_line = true
			} else if err != nil {
				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			}

			lines++

			fmt.Print(line)

			if *nLines == lines || last_line {
				break
			}

		}

		/* Print multi file seperator */
		if !*quiet && numfiles > 1 && i < len(flag.Args())-1 {
			fmt.Println("")
		}
	}
}
<|endoftext|>


<commit_before>// Package help implements a simple GitHub wiki miner and output formatter.
package help

import (
	"fmt"
	"io"
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/mitchellh/go-wordwrap"
)

// TODO: HelpTopics could be interactive
// TODO: walk nodes to highlight inline code etc
// TODO: ~/.apex.json user config, use here for color mapping etc

// colors.
const (
	none   = 0
	red    = 31
	green  = 32
	yellow = 33
	blue   = 34
	gray   = 37
)

// Endpoint used to lookup help information.
var Endpoint = "https://github.com/apex/apex/wiki"

// Help outputs topic categories.
func HelpTopics(w io.Writer) error {
	doc, err := goquery.NewDocument(Endpoint)
	if err != nil {
		return err
	}

	fmt.Fprintf(w, "\n")
	defer fmt.Fprintf(w, "\n")

	doc.Find(`#wiki-content .markdown-body ul li`).Each(func(i int, s *goquery.Selection) {
		strs := strings.Split(text(s.Text()), ": ")
		fmt.Fprintf(w, "  \033[%dm%s\033[0m: %s \n", blue, strs[0], strs[1])
	})

	fmt.Fprintf(w, "\n  Use `apex help <topic>` to view a topic.\n")

	return nil
}

// HelpTopic outputs topic for the given `topic`.
func HelpTopic(topic string, w io.Writer) error {
	doc, err := goquery.NewDocument(fmt.Sprintf("%s/%s", Endpoint, topic))
	if err != nil {
		return err
	}

	fmt.Fprintf(w, "\n")
	defer fmt.Fprintf(w, "\n")

	doc.Find(`#wiki-content .markdown-body *`).Each(func(i int, s *goquery.Selection) {
		switch node := s.Get(0); node.Data {
		case "h1":
			fmt.Printf(" \033[%dm# %s\033[0m\n\n", blue, text(s.Text()))
		case "h2":
			fmt.Printf(" \033[%dm## %s\033[0m\n\n", blue, text(s.Text()))
		case "h3":
			fmt.Printf(" \033[%dm### %s\033[0m\n\n", blue, text(s.Text()))
		case "p":
			fmt.Printf("\033[%dm%s\033[0m\n\n", none, indent(text(s.Text()), 1))
		case "div", "pre":
			if s.HasClass("highlight") || node.Data == "pre" {
				fmt.Printf("\033[%dm%s\033[0m\n\n", gray, indent(text(s.Text()), 2))
			}
		}
	})

	return nil
}

// text trim and wrap.
func text(s string) string {
	return wordwrap.WrapString(strings.TrimSpace(s), 80)
}

// indent string N times.
func indent(s string, n int) string {
	i := strings.Repeat("  ", n)
	return i + strings.Replace(s, "\n", "\n"+i, -1)
}
<commit_msg>refactor help to support links<commit_after>// Package help implements a simple GitHub wiki miner and output formatter.
package help

import (
	"fmt"
	"io"
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/mitchellh/go-wordwrap"
	"golang.org/x/net/html"
)

// TODO: handle invalid page
// TODO: HelpTopics could be interactive
// TODO: ~/.apex.json user config, use here for color mapping etc

// colors.
const (
	none   = 0
	red    = 31
	green  = 32
	yellow = 33
	blue   = 34
	gray   = 37
)

// Endpoint used to lookup help information.
var Endpoint = "https://github.com/apex/apex/wiki"

// Help outputs topic categories.
func HelpTopics(w io.Writer) error {
	doc, err := goquery.NewDocument(Endpoint)
	if err != nil {
		return err
	}

	fmt.Fprintf(w, "\n")
	defer fmt.Fprintf(w, "\n")

	doc.Find(`#wiki-content .markdown-body ul li`).Each(func(i int, s *goquery.Selection) {
		strs := strings.Split(text(s), ": ")
		fmt.Fprintf(w, "  \033[%dm%s\033[0m: %s \n", blue, strs[0], strs[1])
	})

	fmt.Fprintf(w, "\n  Use `apex help <topic>` to view a topic.\n")

	return nil
}

// HelpTopic outputs topic for the given `topic`.
func HelpTopic(topic string, w io.Writer) error {
	doc, err := goquery.NewDocument(fmt.Sprintf("%s/%s", Endpoint, topic))
	if err != nil {
		return err
	}

	fmt.Fprintln(w)
	fmt.Fprint(w, nodes(doc.Find(`#wiki-content .markdown-body`)))
	fmt.Fprintln(w)

	return nil
}

// nodes returns a string representation of the selection's children.
func nodes(s *goquery.Selection) string {
	return strings.Join(s.Children().Map(node), "")
}

// contents returns a string representation of the selection's contents.
func contents(s *goquery.Selection) string {
	return strings.Join(s.Contents().Map(node), "")
}

// node returns a string representation of the selection.
func node(i int, s *goquery.Selection) string {
	switch node := s.Get(0); {
	case node.Data == "h1":
		return fmt.Sprintf(" \033[%dm# %s\033[0m\n\n", blue, text(s))
	case node.Data == "h2":
		return fmt.Sprintf(" \033[%dm## %s\033[0m\n\n", blue, text(s))
	case node.Data == "h3":
		return fmt.Sprintf(" \033[%dm### %s\033[0m\n\n", blue, text(s))
	case node.Data == "p":
		return fmt.Sprintf("\033[%dm%s\033[0m\n\n", none, indent(text(s), 1))
	case node.Data == "pre" || s.HasClass("highlight"):
		return fmt.Sprintf("\033[%dm%s\033[0m\n\n", gray, indent(text(s), 2))
	case node.Data == "a":
		return fmt.Sprintf("%s (%s) ", s.Text(), s.AttrOr("href", "missing link"))
	case node.Data == "li":
		return fmt.Sprintf("  • %s\n", contents(s))
	case node.Data == "ul":
		return fmt.Sprintf("%s\n", nodes(s))
	case node.Type == html.TextNode:
		return strings.TrimSpace(node.Data)
	default:
		return ""
	}
}

// text of selection, trimmed and wrapped.
func text(s *goquery.Selection) string {
	return wordwrap.WrapString(strings.TrimSpace(s.Text()), 80)
}

// indent string N times.
func indent(s string, n int) string {
	i := strings.Repeat("  ", n)
	return i + strings.Replace(s, "\n", "\n"+i, -1)
}
<|endoftext|>


<commit_before>package main

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

// TODO finish this test
// TestGetMeminfo test the returned fields values of `getHostinfo()`
// func TestGetHostinfo(t *testing.T) {
// 	// setup the faking of `host.Info()`
// 	oldHostInfo := hostInfo
// 	hostInfo = func() (*host.InfoStat, error) {
// 		ret := &host.InfoStat{
// 			Hostname:        "hostname",
// 			OS:              "Os",
// 			Platform:        "Platform",
// 			PlatformVersion: "10.00",
// 		}
// 		return ret, nil
// 	}
//
// 	// test
// 	expected := hostinfo{
// 		hostname:        "hostname",
// 		os:              "Os",
// 		platform:        "Platform",
// 		platformVersion: "10.00",
// 		domainname:      "domainname",
// 		osRelease:       "1.0.0",
// 		arch:            "x86_64",
// 	}
// 	actual := getHostinfo()
//
// 	assert.Equal(t, expected, actual, "`getHostinfo()` should be equal to --> hostinfo{hostname:\"hostname\", domainname:\"domainname\", os:\"Os\", osRelease:\"1.0.0\", platform:\"Platform\", platformVersion:\"10.00\", arch:\"x86_64\"}")
//
// 	// teardown
// 	hostInfo = oldHostInfo
// }

// TestGetHostinfoType test if `getHostinfo()` return a `procinfo` type and if each fields have the correct types
func TestGetHostinfoType(t *testing.T) {
	expected := hostinfo{
		hostname:        "", // the result values of the fields are not tested
		domainname:      "",
		os:              "",
		osRelease:       "",
		platform:        "",
		platformVersion: "",
		arch:            "",
	}
	actual := getHostinfo()

	assert.IsType(t, expected, actual, "`getHostinfo()` should return a `hostinfo` type")
	assert.IsType(t, expected.hostname, actual.hostname, "`getHostinfo()` should return a `hostname` field with a string type")
	assert.IsType(t, expected.domainname, actual.domainname, "`getHostinfo()` should return a `domainname` field with a string type")
	assert.IsType(t, expected.os, actual.os, "`getHostinfo()` should return a `os` field with a string type")
	assert.IsType(t, expected.osRelease, actual.osRelease, "`getHostinfo()` should return a `osRelease` field with a string type")
	assert.IsType(t, expected.platform, actual.platform, "`getHostinfo()` should return a `platform` field with a string type")
	assert.IsType(t, expected.platformVersion, actual.platformVersion, "`getHostinfo()` should return a `platformVersion` field with a string type")
	assert.IsType(t, expected.arch, actual.arch, "`getHostinfo()` should return a `arch` field with a string type")
}

// TestGetUptime test the returned value of `getUptime()`
func TestGetUptime(t *testing.T) {
	// setup the faking of `cpu.Percent()`
	oldHostUptime := hostUptime
	hostUptime = func() (uint64, error) {
		ret := uint64(86400) // time.Duration to string conversion is implicitly tested --> 24h * 60m * 60s = 86400
		return ret, nil
	}

	// test
	expected := "24h0m0s"
	actual := getUptime()

	assert.Equal(t, expected, actual, "`getUptime` should be equal to --> \"24h0m0s\"")

	// teardown
	hostUptime = oldHostUptime
}

// TestGetUptimeType test if `getUptime` return a value with a string type
func TestGetUptimeType(t *testing.T) {
	expected := "" // the result value is not tested
	actual := getUptime()

	assert.IsType(t, expected, actual, "`getUptime` should return a string`")
}
<commit_msg>Update testing<commit_after>package main

import (
	"syscall"
	"testing"

	"github.com/shirou/gopsutil/host"
	"github.com/stretchr/testify/assert"
)

// TODO finish this test
// TestGetMeminfo test the returned fields values of `getHostinfo()`
func TestGetHostinfo(t *testing.T) {
	// setup the faking of `host.Info()`
	oldHostInfo := hostInfo
	hostInfo = func() (*host.InfoStat, error) {
		ret := &host.InfoStat{
			Hostname:        "abc",
			OS:              "linux", // we need a `Linux` OS if we want to test datas retrivied by `getUname()`
			Platform:        "abc",
			PlatformVersion: "abc",
		}
		return ret, nil
	}
	oldgetUname := getUname
	getUname = func() (syscall.Utsname, error) {
		int8string := [65]int8{ // See the ASCII table: 97 = "a" ; 98 ="b" ; 99 = "c"
			64, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
			64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
			64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
		} //TODO if possible avoid this complicate way to test because of [65]int8 requirement by syscall.Utsname
		ret := syscall.Utsname{
			Release:    int8string,
			Machine:    int8string,
			Domainname: int8string,
		}
		return ret, nil
	}

	// test
	expected := hostinfo{
		hostname:        "abc",
		os:              "Linux",
		platform:        "Abc",
		platformVersion: "abc",
		domainname:      "@0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ@abcdefghijklmnopqrstuvwxyz", //
		osRelease:       "@0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ@abcdefghijklmnopqrstuvwxyz", //
		arch:            "@0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ@abcdefghijklmnopqrstuvwxyz", //
	}
	actual := getHostinfo()

	assert.Equal(t, expected, actual, "`getHostinfo()` should be equal to main.hostinfo{hostname:\"hostname\", domainname:\"domainname\", os:\"Os\", osRelease:\"1.0.0\", platform:\"Platform\", platformVersion:\"10.00\", arch:\"x86_64\"}")

	// teardown
	hostInfo = oldHostInfo
	getUname = oldgetUname
}

// TestGetHostinfoType test if `getHostinfo()` return a `procinfo` type and if each fields have the correct types
func TestGetHostinfoType(t *testing.T) {
	expected := hostinfo{
		hostname:        "", // the result values of the fields are not tested
		domainname:      "",
		os:              "",
		osRelease:       "",
		platform:        "",
		platformVersion: "",
		arch:            "",
	}
	actual := getHostinfo()

	assert.IsType(t, expected, actual, "`getHostinfo()` should return a `hostinfo` type")
	assert.IsType(t, expected.hostname, actual.hostname, "`getHostinfo()` should return a `hostname` field with a string type")
	assert.IsType(t, expected.domainname, actual.domainname, "`getHostinfo()` should return a `domainname` field with a string type")
	assert.IsType(t, expected.os, actual.os, "`getHostinfo()` should return a `os` field with a string type")
	assert.IsType(t, expected.osRelease, actual.osRelease, "`getHostinfo()` should return a `osRelease` field with a string type")
	assert.IsType(t, expected.platform, actual.platform, "`getHostinfo()` should return a `platform` field with a string type")
	assert.IsType(t, expected.platformVersion, actual.platformVersion, "`getHostinfo()` should return a `platformVersion` field with a string type")
	assert.IsType(t, expected.arch, actual.arch, "`getHostinfo()` should return a `arch` field with a string type")
}

// TestGetUptime test the returned value of `getUptime()`
func TestGetUptime(t *testing.T) {
	// setup the faking of `cpu.Percent()`
	oldHostUptime := hostUptime
	hostUptime = func() (uint64, error) {
		ret := uint64(86400) // time.Duration to string conversion is implicitly tested --> 24h * 60m * 60s = 86400
		return ret, nil
	}

	// test
	expected := "24h0m0s"
	actual := getUptime()

	assert.Equal(t, expected, actual, "`getUptime` should be equal to --> \"24h0m0s\"")

	// teardown
	hostUptime = oldHostUptime
}

// TestGetUptimeType test if `getUptime` return a value with a string type
func TestGetUptimeType(t *testing.T) {
	expected := "" // the result value is not tested
	actual := getUptime()

	assert.IsType(t, expected, actual, "`getUptime` should return a string`")
}
<|endoftext|>


<commit_before>/*
library for connecting go applications into i2p with relative ease

implements `net.Listener`, `net.Conn`, `net.Addr`, `net.Dial` that goes over i2p

    package main

    import (
      "github.com/majestrate/i2p-tools/lib/i2p"
      "fmt"
      "net"
      "net/http"
      "path/filepath"
    )

    // see i2p.Session interface for more usage

    func main() {
       var err error
       var sess i2p.Session
       // connect to an i2p router
       // you can pass in "" to generate a transient session that doesn't save the destination keys
       sess, err = i2p.NewSessionEasy("127.0.0.1:7656", filepath.Join("some", "path", "to", "privatekey.txt"))
       if err != nil {
         log.Println("failed to open connection to i2p router", err)
         return
       }
       // close our connection to i2p when done
       defer sess.Close()

       // i2p.Session implements net.Listener
       // we can pass it to http.Serve to serve an http server via i2p
       fmt.Printf("http server going up at http://%s/", sess.B32())
       err = http.Serve(sess, nil)
    }

*/
package i2p
<commit_msg>more fixups<commit_after>/*
library for connecting go applications into i2p with relative ease

implements `net.Listener`, `net.Conn`, `net.Addr`, `net.Dial` that goes over i2p

    package main

    import (
      "github.com/majestrate/i2p-tools/lib/i2p"
      "fmt"
      "net"
      "net/http"
      "path/filepath"
    )

    // see i2p.Session interface for more usage

    func main() {
       var err error
       var sess i2p.Session
       // connect to an i2p router
       // you can pass in "" to generate a transient session that doesn't save the destination keys
       sess, err = i2p.NewSessionEasy("127.0.0.1:7656", filepath.Join("some", "path", "to", "privatekey.txt"))
       if err != nil {
         fmt.Printf("failed to open connection to i2p router: %s", err.Error())
         return
       }
       // close our connection to i2p when done
       defer sess.Close()

       // i2p.Session implements net.Listener
       // we can pass it to http.Serve to serve an http server via i2p
       fmt.Printf("http server going up at http://%s/", sess.B32())
       err = http.Serve(sess, nil)
    }

*/
package i2p
<|endoftext|>


<commit_before>package sudoku

import (
	"fmt"
	"math/rand"
)

type guessTechnique struct {
	*basicSolveTechnique
}

func (self *guessTechnique) HumanLikelihood() float64 {
	return self.difficultyHelper(100000000.0)
}

func (self *guessTechnique) Description(step *SolveStep) string {
	return fmt.Sprintf("we have no other moves to make, so we randomly pick a cell with the smallest number of possibilities, %s, and pick one of its possibilities", step.TargetCells.Description())
}

func (self *guessTechnique) Find(grid *Grid) []*SolveStep {

	getter := grid.queue().NewGetter()

	var results []*SolveStep

	for {
		obj := getter.Get()
		if obj == nil {
			break
		}

		//This WILL happen, since guess will return a bunch of possible guesses you could make.
		if obj.rank() > 3 {
			//Given that this WILL happen, it's important to return results so far, whatever they are.
			break
		}

		//Convert RankedObject to a cell
		cell := obj.(*Cell)
		possibilities := cell.Possibilities()

		if len(possibilities) == 0 {
			//Not entirely sure why this would happen, but it can...
			continue
		}

		num := possibilities[rand.Intn(len(possibilities))]
		step := newFillSolveStep(cell, num, self)

		//We're going to abuse pointerNums and use it to point out the other numbers we COULD have used.
		step.PointerNums = IntSlice(possibilities).Difference(IntSlice{num})
		if step.IsUseful(grid) {
			results = append(results, step)
		}
	}

	return results
}
<commit_msg>Guess implements new Find signature<commit_after>package sudoku

import (
	"fmt"
	"math/rand"
)

type guessTechnique struct {
	*basicSolveTechnique
}

func (self *guessTechnique) HumanLikelihood() float64 {
	return self.difficultyHelper(100000000.0)
}

func (self *guessTechnique) Description(step *SolveStep) string {
	return fmt.Sprintf("we have no other moves to make, so we randomly pick a cell with the smallest number of possibilities, %s, and pick one of its possibilities", step.TargetCells.Description())
}

func (self *guessTechnique) Find(grid *Grid, results chan *SolveStep, done chan bool) {

	getter := grid.queue().NewGetter()

	for {

		select {
		case <-done:
			return
		default:
		}

		obj := getter.Get()
		if obj == nil {
			break
		}

		//This WILL happen, since guess will return a bunch of possible guesses you could make.
		if obj.rank() > 3 {
			//Given that this WILL happen, it's important to return results so far, whatever they are.
			break
		}

		//Convert RankedObject to a cell
		cell := obj.(*Cell)
		possibilities := cell.Possibilities()

		if len(possibilities) == 0 {
			//Not entirely sure why this would happen, but it can...
			continue
		}

		num := possibilities[rand.Intn(len(possibilities))]
		step := newFillSolveStep(cell, num, self)

		//We're going to abuse pointerNums and use it to point out the other numbers we COULD have used.
		step.PointerNums = IntSlice(possibilities).Difference(IntSlice{num})
		if step.IsUseful(grid) {
			select {
			case results <- step:
			case <-done:
				return
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"net"
	"net/http"
	"sync"

	"github.com/giantswarm/mayu/hostmgr"
	"github.com/giantswarm/mayu/logging"
	"github.com/golang/glog"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)

type pxeManagerT struct {
	cluster *hostmgr.Cluster
	DNSmasq *DNSmasqInstance

	mu *sync.Mutex

	router *mux.Router
}

const defaultEtcdQuorumSize = 3

func defaultPXEManager(cluster *hostmgr.Cluster) (*pxeManagerT, error) {
	mgr := &pxeManagerT{
		cluster: cluster,
		DNSmasq: NewDNSmasq("/tmp/dnsmasq.mayu", conf),
		mu:      new(sync.Mutex),
	}

	if mgr.cluster.Config.EtcdDiscoveryURL == "" {
		mgr.cluster.GenerateEtcdDiscoveryURL(defaultEtcdQuorumSize)
		mgr.cluster.Commit("generated etcd discovery url")
	}

	return mgr, nil
}

func withSerialParam(serialHandler func(serial string, w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		params := mux.Vars(r)
		serialHandler(params["serial"], w, r)
	}
}

func (mgr *pxeManagerT) startIPXEserver() error {
	mgr.router = mux.NewRouter()

	// first stage ipxe boot script
	mgr.router.Methods("GET").PathPrefix("/ipxebootscript").HandlerFunc(ipxeBootScript)
	mgr.router.Methods("GET").PathPrefix("/first-stage-script/{serial}").HandlerFunc(mgr.firstStageScriptGenerator)

	// used by the first-stage-script:
	mgr.router.Methods("GET").PathPrefix("/hostinfo-helper").HandlerFunc(mgr.infoPusher)
	mgr.router.Methods("POST").PathPrefix("/final-cloud-config.yaml").HandlerFunc(mgr.cloudConfigGenerator)

	mgr.router.Methods("PUT").PathPrefix("/admin/host/{serial}/boot_complete").HandlerFunc(withSerialParam(mgr.bootComplete))
	mgr.router.Methods("PUT").PathPrefix("/admin/host/{serial}/set_installed").HandlerFunc(withSerialParam(mgr.setInstalled))
	mgr.router.Methods("PUT").PathPrefix("/admin/host/{serial}/set_metadata").HandlerFunc(withSerialParam(mgr.setMetadata))
	mgr.router.Methods("PUT").PathPrefix("/admin/host/{serial}/mark_fresh").HandlerFunc(withSerialParam(mgr.markFresh))
	mgr.router.Methods("PUT").PathPrefix("/admin/host/{serial}/set_provider_id").HandlerFunc(withSerialParam(mgr.setProviderId))
	mgr.router.Methods("PUT").PathPrefix("/admin/host/{serial}/set_ipmi_addr").HandlerFunc(withSerialParam(mgr.setIPMIAddr))
	mgr.router.Methods("PUT").PathPrefix("/admin/host/{serial}/set_cabinet").HandlerFunc(withSerialParam(mgr.setCabinet))

	// boring stuff
	mgr.router.Methods("GET").PathPrefix("/admin/hosts").HandlerFunc(mgr.hostsList)
	mgr.router.Methods("GET").PathPrefix("/images").HandlerFunc(imagesHandler)

	// add welcome handler for debugging
	mgr.router.Path("/").HandlerFunc(mgr.welcomeHandler)

	// serve static files like infopusher and mayuctl etc.
	mgr.router.PathPrefix("/").Handler(http.FileServer(http.Dir(conf.StaticHTMLPath)))

	// serve assets for yochu like etcd, fleet and docker
	mgr.router.PathPrefix("/yochu").Handler(http.FileServer(http.Dir(conf.YochuPath)))

	glogWrapper := logging.NewGlogWrapper(8)
	loggedRouter := handlers.LoggingHandler(glogWrapper, mgr.router)

	glog.V(8).Infoln(fmt.Sprintf("starting iPXE server at %s:%d", conf.HTTPBindAddr, conf.HTTPPort))

	if conf.NoSecure {
		err := http.ListenAndServe(fmt.Sprintf("%s:%d", conf.HTTPBindAddr, conf.HTTPPort), loggedRouter)
		if err != nil {
			return err
		}
	} else {
		err := http.ListenAndServeTLS(fmt.Sprintf("%s:%d", conf.HTTPBindAddr, conf.HTTPPort), conf.HTTPSCertFile, conf.HTTPSKeyFile, loggedRouter)
		if err != nil {
			return err
		}
	}

	return nil
}

func (mgr *pxeManagerT) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	mgr.router.ServeHTTP(w, r)
}

func (mgr *pxeManagerT) updateDNSmasqs() error {
	mgr.mu.Lock()
	defer mgr.mu.Unlock()

	conf.Network.StaticHosts = []hostmgr.IPMac{}
	conf.Network.IgnoredHosts = []string{}

	ignoredHostPredicate := func(host *hostmgr.Host) bool {
		// ignore hosts that are installed or running
		return host.State == hostmgr.Installed || host.State == hostmgr.Running
	}

	for host := range mgr.cluster.FilterHostsFunc(ignoredHostPredicate) {
		for _, macAddr := range host.MacAddresses {
			conf.Network.IgnoredHosts = append(conf.Network.IgnoredHosts, macAddr)
		}
	}

	err := mgr.DNSmasq.updateConf(conf.Network)
	if err != nil {
		return err
	}
	err = mgr.DNSmasq.Restart()
	if err != nil {
		return err
	}

	return nil
}

func (mgr *pxeManagerT) Start() error {
	err := mgr.DNSmasq.Start()
	if err != nil {
		return err
	}

	err = mgr.updateDNSmasqs()
	if err != nil {
		return err
	}

	return mgr.startIPXEserver()
}

func (mgr *pxeManagerT) getNextProfile() string {
	profileCount := mgr.cluster.GetProfileCount()

	for _, profile := range conf.Profiles {
		if profileCount[profile.Name] < profile.Quantity {
			return profile.Name
		}
	}
	return ""
}

func (mgr *pxeManagerT) getNextInternalIP() net.IP {
	assignedIPs := map[string]struct{}{}
	for _, host := range mgr.cluster.GetAllHosts() {
		assignedIPs[host.InternalAddr.String()] = struct{}{}
	}

	IPisAvailable := func(ip net.IP) bool {
		_, exists := assignedIPs[ip.String()]
		return !exists
	}

	currentIP := net.ParseIP(conf.Network.IPRange.Start)
	rangeEnd := net.ParseIP(conf.Network.IPRange.End)

	for ; ; ipLessThanOrEqual(currentIP, rangeEnd) {
		if IPisAvailable(currentIP) {
			return currentIP
		}
		currentIP = incIP(currentIP)
	}

	panic(errors.New("unable to get a free ip"))
	return net.IP{}
}
<commit_msg>fix handling of yochu asset route<commit_after>package main

import (
	"errors"
	"fmt"
	"net"
	"net/http"
	"sync"

	"github.com/giantswarm/mayu/hostmgr"
	"github.com/giantswarm/mayu/logging"
	"github.com/golang/glog"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)

type pxeManagerT struct {
	cluster *hostmgr.Cluster
	DNSmasq *DNSmasqInstance

	mu *sync.Mutex

	router *mux.Router
}

const defaultEtcdQuorumSize = 3

func defaultPXEManager(cluster *hostmgr.Cluster) (*pxeManagerT, error) {
	mgr := &pxeManagerT{
		cluster: cluster,
		DNSmasq: NewDNSmasq("/tmp/dnsmasq.mayu", conf),
		mu:      new(sync.Mutex),
	}

	if mgr.cluster.Config.EtcdDiscoveryURL == "" {
		mgr.cluster.GenerateEtcdDiscoveryURL(defaultEtcdQuorumSize)
		mgr.cluster.Commit("generated etcd discovery url")
	}

	return mgr, nil
}

func withSerialParam(serialHandler func(serial string, w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		params := mux.Vars(r)
		serialHandler(params["serial"], w, r)
	}
}

func (mgr *pxeManagerT) startIPXEserver() error {
	mgr.router = mux.NewRouter()

	// first stage ipxe boot script
	mgr.router.Methods("GET").PathPrefix("/ipxebootscript").HandlerFunc(ipxeBootScript)
	mgr.router.Methods("GET").PathPrefix("/first-stage-script/{serial}").HandlerFunc(mgr.firstStageScriptGenerator)

	// used by the first-stage-script:
	mgr.router.Methods("GET").PathPrefix("/hostinfo-helper").HandlerFunc(mgr.infoPusher)
	mgr.router.Methods("POST").PathPrefix("/final-cloud-config.yaml").HandlerFunc(mgr.cloudConfigGenerator)

	mgr.router.Methods("PUT").PathPrefix("/admin/host/{serial}/boot_complete").HandlerFunc(withSerialParam(mgr.bootComplete))
	mgr.router.Methods("PUT").PathPrefix("/admin/host/{serial}/set_installed").HandlerFunc(withSerialParam(mgr.setInstalled))
	mgr.router.Methods("PUT").PathPrefix("/admin/host/{serial}/set_metadata").HandlerFunc(withSerialParam(mgr.setMetadata))
	mgr.router.Methods("PUT").PathPrefix("/admin/host/{serial}/mark_fresh").HandlerFunc(withSerialParam(mgr.markFresh))
	mgr.router.Methods("PUT").PathPrefix("/admin/host/{serial}/set_provider_id").HandlerFunc(withSerialParam(mgr.setProviderId))
	mgr.router.Methods("PUT").PathPrefix("/admin/host/{serial}/set_ipmi_addr").HandlerFunc(withSerialParam(mgr.setIPMIAddr))
	mgr.router.Methods("PUT").PathPrefix("/admin/host/{serial}/set_cabinet").HandlerFunc(withSerialParam(mgr.setCabinet))

	// boring stuff
	mgr.router.Methods("GET").PathPrefix("/admin/hosts").HandlerFunc(mgr.hostsList)
	mgr.router.Methods("GET").PathPrefix("/images").HandlerFunc(imagesHandler)

	// serve assets for yochu like etcd, fleet and docker
	mgr.router.PathPrefix("/yochu").Handler(http.StripPrefix("/yochu", http.FileServer(http.Dir(conf.YochuPath))))

	// add welcome handler for debugging
	mgr.router.Path("/").HandlerFunc(mgr.welcomeHandler)

	// serve static files like infopusher and mayuctl etc.
	mgr.router.PathPrefix("/").Handler(http.FileServer(http.Dir(conf.StaticHTMLPath)))

	glogWrapper := logging.NewGlogWrapper(8)
	loggedRouter := handlers.LoggingHandler(glogWrapper, mgr.router)

	glog.V(8).Infoln(fmt.Sprintf("starting iPXE server at %s:%d", conf.HTTPBindAddr, conf.HTTPPort))

	if conf.NoSecure {
		err := http.ListenAndServe(fmt.Sprintf("%s:%d", conf.HTTPBindAddr, conf.HTTPPort), loggedRouter)
		if err != nil {
			return err
		}
	} else {
		err := http.ListenAndServeTLS(fmt.Sprintf("%s:%d", conf.HTTPBindAddr, conf.HTTPPort), conf.HTTPSCertFile, conf.HTTPSKeyFile, loggedRouter)
		if err != nil {
			return err
		}
	}

	return nil
}

func (mgr *pxeManagerT) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	mgr.router.ServeHTTP(w, r)
}

func (mgr *pxeManagerT) updateDNSmasqs() error {
	mgr.mu.Lock()
	defer mgr.mu.Unlock()

	conf.Network.StaticHosts = []hostmgr.IPMac{}
	conf.Network.IgnoredHosts = []string{}

	ignoredHostPredicate := func(host *hostmgr.Host) bool {
		// ignore hosts that are installed or running
		return host.State == hostmgr.Installed || host.State == hostmgr.Running
	}

	for host := range mgr.cluster.FilterHostsFunc(ignoredHostPredicate) {
		for _, macAddr := range host.MacAddresses {
			conf.Network.IgnoredHosts = append(conf.Network.IgnoredHosts, macAddr)
		}
	}

	err := mgr.DNSmasq.updateConf(conf.Network)
	if err != nil {
		return err
	}
	err = mgr.DNSmasq.Restart()
	if err != nil {
		return err
	}

	return nil
}

func (mgr *pxeManagerT) Start() error {
	err := mgr.DNSmasq.Start()
	if err != nil {
		return err
	}

	err = mgr.updateDNSmasqs()
	if err != nil {
		return err
	}

	return mgr.startIPXEserver()
}

func (mgr *pxeManagerT) getNextProfile() string {
	profileCount := mgr.cluster.GetProfileCount()

	for _, profile := range conf.Profiles {
		if profileCount[profile.Name] < profile.Quantity {
			return profile.Name
		}
	}
	return ""
}

func (mgr *pxeManagerT) getNextInternalIP() net.IP {
	assignedIPs := map[string]struct{}{}
	for _, host := range mgr.cluster.GetAllHosts() {
		assignedIPs[host.InternalAddr.String()] = struct{}{}
	}

	IPisAvailable := func(ip net.IP) bool {
		_, exists := assignedIPs[ip.String()]
		return !exists
	}

	currentIP := net.ParseIP(conf.Network.IPRange.Start)
	rangeEnd := net.ParseIP(conf.Network.IPRange.End)

	for ; ; ipLessThanOrEqual(currentIP, rangeEnd) {
		if IPisAvailable(currentIP) {
			return currentIP
		}
		currentIP = incIP(currentIP)
	}

	panic(errors.New("unable to get a free ip"))
	return net.IP{}
}
<|endoftext|>