<commit_before>package raft

import (
	"errors"
	"sort"
)

const none = -1

type messageType int

const (
	msgHup messageType = iota
	msgBeat
	msgProp
	msgApp
	msgAppResp
	msgVote
	msgVoteResp
)

var mtmap = [...]string{
	msgHup:      "msgHup",
	msgBeat:     "msgBeat",
	msgProp:     "msgProp",
	msgApp:      "msgApp",
	msgAppResp:  "msgAppResp",
	msgVote:     "msgVote",
	msgVoteResp: "msgVoteResp",
}

func (mt messageType) String() string {
	return mtmap[int(mt)]
}

var errNoLeader = errors.New("no leader")

const (
	stateFollower stateType = iota
	stateCandidate
	stateLeader
)

type stateType int

var stmap = [...]string{
	stateFollower:  "stateFollower",
	stateCandidate: "stateCandidate",
	stateLeader:    "stateLeader",
}

func (st stateType) String() string {
	return stmap[int(st)]
}

type Message struct {
	Type     messageType
	To       int
	From     int
	Term     int
	LogTerm  int
	Index    int
	PrevTerm int
	Entries  []Entry
	Commit   int
}

type index struct {
	match, next int
}

func (in *index) update(n int) {
	in.match = n
	in.next = n + 1
}

func (in *index) decr() {
	if in.next--; in.next < 1 {
		in.next = 1
	}
}

type stateMachine struct {
	addr int

	// the term we are participating in at any time
	term int

	// who we voted for in term
	vote int

	// the log
	log *log

	ins map[int]*index

	state stateType

	votes map[int]bool

	msgs []Message

	// the leader addr
	lead int

	// pending reconfiguration
	pendingConf bool
}

func newStateMachine(addr int, peer []int) *stateMachine {
	sm := &stateMachine{addr: addr, log: newLog(), ins: make(map[int]*index)}
	for p := range peer {
		sm.ins[p] = &index{}
	}
	sm.reset()
	return sm
}

func (sm *stateMachine) canStep(m Message) bool {
	if m.Type == msgProp {
		return sm.lead != none
	}
	return true
}

func (sm *stateMachine) poll(addr int, v bool) (granted int) {
	if _, ok := sm.votes[addr]; !ok {
		sm.votes[addr] = v
	}
	for _, vv := range sm.votes {
		if vv {
			granted++
		}
	}
	return granted
}

// send persists state to stable storage and then sends to its mailbox.
func (sm *stateMachine) send(m Message) {
	m.From = sm.addr
	m.Term = sm.term
	sm.msgs = append(sm.msgs, m)
}

// sendAppend sends RRPC, with entries to the given peer.
func (sm *stateMachine) sendAppend(to int) {
	in := sm.ins[to]
	m := Message{}
	m.Type = msgApp
	m.To = to
	m.Index = in.next - 1
	m.LogTerm = sm.log.term(in.next - 1)
	m.Entries = sm.log.entries(in.next)
	m.Commit = sm.log.committed
	sm.send(m)
}

// bcastAppend sends RRPC, with entries to all peers that are not up-to-date according to sm.mis.
func (sm *stateMachine) bcastAppend() {
	for i := range sm.ins {
		if i == sm.addr {
			continue
		}
		sm.sendAppend(i)
	}
}

func (sm *stateMachine) maybeCommit() bool {
	// TODO(bmizerany): optimize.. Currently naive
	mis := make([]int, len(sm.ins))
	for i := range mis {
		mis[i] = sm.ins[i].match
	}
	sort.Sort(sort.Reverse(sort.IntSlice(mis)))
	mci := mis[sm.q()-1]

	return sm.log.maybeCommit(mci, sm.term)
}

// nextEnts returns the appliable entries and updates the applied index
func (sm *stateMachine) nextEnts() (ents []Entry) {
	return sm.log.nextEnts()
}

func (sm *stateMachine) reset() {
	sm.lead = none
	sm.vote = none
	sm.votes = make(map[int]bool)
	for i := range sm.ins {
		sm.ins[i] = &index{next: sm.log.lastIndex() + 1}
		if i == sm.addr {
			sm.ins[i].match = sm.log.lastIndex()
		}
	}
}

func (sm *stateMachine) q() int {
	return len(sm.ins)/2 + 1
}

func (sm *stateMachine) becomeFollower(term, lead int) {
	sm.reset()
	sm.term = term
	sm.lead = lead
	sm.state = stateFollower
}

func (sm *stateMachine) becomeCandidate() {
	// TODO(xiangli) remove the panic when the raft implementation is stable
	if sm.state == stateLeader {
		panic("invalid transition [leader -> candidate]")
	}
	sm.reset()
	sm.term++
	sm.vote = sm.addr
	sm.state = stateCandidate
}

func (sm *stateMachine) becomeLeader() {
	// TODO(xiangli) remove the panic when the raft implementation is stable
	if sm.state == stateFollower {
		panic("invalid transition [follower -> leader]")
	}
	sm.reset()
	sm.lead = sm.addr
	sm.state = stateLeader
}

func (sm *stateMachine) Msgs() []Message {
	msgs := sm.msgs
	sm.msgs = make([]Message, 0)

	return msgs
}

func (sm *stateMachine) Step(m Message) {
	switch m.Type {
	case msgHup:
		sm.becomeCandidate()
		if sm.q() == sm.poll(sm.addr, true) {
			sm.becomeLeader()
			return
		}
		for i := range sm.ins {
			if i == sm.addr {
				continue
			}
			lasti := sm.log.lastIndex()
			sm.send(Message{To: i, Type: msgVote, Index: lasti, LogTerm: sm.log.term(lasti)})
		}
		return
	case msgBeat:
		if sm.state != stateLeader {
			return
		}
		sm.bcastAppend()
		return
	case msgProp:
		if len(m.Entries) != 1 {
			panic("unexpected length(entries) of a msgProp")
		}

		switch sm.lead {
		case sm.addr:
			e := m.Entries[0]
			if e.Type == config {
				if sm.pendingConf {
					// todo: deny
					return
				}
				sm.pendingConf = true
			}
			e.Term = sm.term

			sm.log.append(sm.log.lastIndex(), e)
			sm.ins[sm.addr].update(sm.log.lastIndex())
			sm.maybeCommit()
			sm.bcastAppend()
		case none:
			panic("msgProp given without leader")
		default:
			m.To = sm.lead
			sm.send(m)
		}
		return
	}

	switch {
	case m.Term > sm.term:
		sm.becomeFollower(m.Term, m.From)
	case m.Term < sm.term:
		// ignore
		return
	}

	handleAppendEntries := func() {
		if sm.log.maybeAppend(m.Index, m.LogTerm, m.Commit, m.Entries...) {
			sm.send(Message{To: m.From, Type: msgAppResp, Index: sm.log.lastIndex()})
		} else {
			sm.send(Message{To: m.From, Type: msgAppResp, Index: -1})
		}
	}

	switch sm.state {
	case stateLeader:
		switch m.Type {
		case msgAppResp:
			if m.Index < 0 {
				sm.ins[m.From].decr()
				sm.sendAppend(m.From)
			} else {
				sm.ins[m.From].update(m.Index)
				if sm.maybeCommit() {
					sm.bcastAppend()
				}
			}
		case msgVote:
			sm.send(Message{To: m.From, Type: msgVoteResp, Index: -1})
		}
	case stateCandidate:
		switch m.Type {
		case msgApp:
			sm.becomeFollower(sm.term, m.From)
			handleAppendEntries()
		case msgVote:
			sm.send(Message{To: m.From, Type: msgVoteResp, Index: -1})
		case msgVoteResp:
			gr := sm.poll(m.From, m.Index >= 0)
			switch sm.q() {
			case gr:
				sm.becomeLeader()
				sm.bcastAppend()
			case len(sm.votes) - gr:
				sm.becomeFollower(sm.term, none)
			}
		}
	case stateFollower:
		switch m.Type {
		case msgApp:
			handleAppendEntries()
		case msgVote:
			if (sm.vote == none || sm.vote == m.From) && sm.log.isUpToDate(m.Index, m.LogTerm) {
				sm.vote = m.From
				sm.send(Message{To: m.From, Type: msgVoteResp, Index: sm.log.lastIndex()})
			} else {
				sm.send(Message{To: m.From, Type: msgVoteResp, Index: -1})
			}
		}
	}
}
<commit_msg>raft: fix pendingConf<commit_after>package raft

import (
	"errors"
	"sort"
)

const none = -1

type messageType int

const (
	msgHup messageType = iota
	msgBeat
	msgProp
	msgApp
	msgAppResp
	msgVote
	msgVoteResp
)

var mtmap = [...]string{
	msgHup:      "msgHup",
	msgBeat:     "msgBeat",
	msgProp:     "msgProp",
	msgApp:      "msgApp",
	msgAppResp:  "msgAppResp",
	msgVote:     "msgVote",
	msgVoteResp: "msgVoteResp",
}

func (mt messageType) String() string {
	return mtmap[int(mt)]
}

var errNoLeader = errors.New("no leader")

const (
	stateFollower stateType = iota
	stateCandidate
	stateLeader
)

type stateType int

var stmap = [...]string{
	stateFollower:  "stateFollower",
	stateCandidate: "stateCandidate",
	stateLeader:    "stateLeader",
}

func (st stateType) String() string {
	return stmap[int(st)]
}

type Message struct {
	Type     messageType
	To       int
	From     int
	Term     int
	LogTerm  int
	Index    int
	PrevTerm int
	Entries  []Entry
	Commit   int
}

type index struct {
	match, next int
}

func (in *index) update(n int) {
	in.match = n
	in.next = n + 1
}

func (in *index) decr() {
	if in.next--; in.next < 1 {
		in.next = 1
	}
}

type stateMachine struct {
	addr int

	// the term we are participating in at any time
	term int

	// who we voted for in term
	vote int

	// the log
	log *log

	ins map[int]*index

	state stateType

	votes map[int]bool

	msgs []Message

	// the leader addr
	lead int

	// pending reconfiguration
	pendingConf bool
}

func newStateMachine(addr int, peer []int) *stateMachine {
	sm := &stateMachine{addr: addr, log: newLog(), ins: make(map[int]*index)}
	for p := range peer {
		sm.ins[p] = &index{}
	}
	sm.reset()
	return sm
}

func (sm *stateMachine) canStep(m Message) bool {
	if m.Type == msgProp {
		return sm.lead != none
	}
	return true
}

func (sm *stateMachine) poll(addr int, v bool) (granted int) {
	if _, ok := sm.votes[addr]; !ok {
		sm.votes[addr] = v
	}
	for _, vv := range sm.votes {
		if vv {
			granted++
		}
	}
	return granted
}

// send persists state to stable storage and then sends to its mailbox.
func (sm *stateMachine) send(m Message) {
	m.From = sm.addr
	m.Term = sm.term
	sm.msgs = append(sm.msgs, m)
}

// sendAppend sends RRPC, with entries to the given peer.
func (sm *stateMachine) sendAppend(to int) {
	in := sm.ins[to]
	m := Message{}
	m.Type = msgApp
	m.To = to
	m.Index = in.next - 1
	m.LogTerm = sm.log.term(in.next - 1)
	m.Entries = sm.log.entries(in.next)
	m.Commit = sm.log.committed
	sm.send(m)
}

// bcastAppend sends RRPC, with entries to all peers that are not up-to-date according to sm.mis.
func (sm *stateMachine) bcastAppend() {
	for i := range sm.ins {
		if i == sm.addr {
			continue
		}
		sm.sendAppend(i)
	}
}

func (sm *stateMachine) maybeCommit() bool {
	// TODO(bmizerany): optimize.. Currently naive
	mis := make([]int, len(sm.ins))
	for i := range mis {
		mis[i] = sm.ins[i].match
	}
	sort.Sort(sort.Reverse(sort.IntSlice(mis)))
	mci := mis[sm.q()-1]

	return sm.log.maybeCommit(mci, sm.term)
}

// nextEnts returns the appliable entries and updates the applied index
func (sm *stateMachine) nextEnts() (ents []Entry) {
	return sm.log.nextEnts()
}

func (sm *stateMachine) reset() {
	sm.lead = none
	sm.vote = none
	sm.votes = make(map[int]bool)
	for i := range sm.ins {
		sm.ins[i] = &index{next: sm.log.lastIndex() + 1}
		if i == sm.addr {
			sm.ins[i].match = sm.log.lastIndex()
		}
	}
}

func (sm *stateMachine) q() int {
	return len(sm.ins)/2 + 1
}

func (sm *stateMachine) becomeFollower(term, lead int) {
	sm.reset()
	sm.term = term
	sm.lead = lead
	sm.state = stateFollower
	sm.pendingConf = false
}

func (sm *stateMachine) becomeCandidate() {
	// TODO(xiangli) remove the panic when the raft implementation is stable
	if sm.state == stateLeader {
		panic("invalid transition [leader -> candidate]")
	}
	sm.reset()
	sm.term++
	sm.vote = sm.addr
	sm.state = stateCandidate
}

func (sm *stateMachine) becomeLeader() {
	// TODO(xiangli) remove the panic when the raft implementation is stable
	if sm.state == stateFollower {
		panic("invalid transition [follower -> leader]")
	}
	sm.reset()
	sm.lead = sm.addr
	sm.state = stateLeader

	for _, e := range sm.log.ents[sm.log.committed:] {
		if e.Type == config {
			sm.pendingConf = true
		}
	}
}

func (sm *stateMachine) Msgs() []Message {
	msgs := sm.msgs
	sm.msgs = make([]Message, 0)

	return msgs
}

func (sm *stateMachine) Step(m Message) {
	switch m.Type {
	case msgHup:
		sm.becomeCandidate()
		if sm.q() == sm.poll(sm.addr, true) {
			sm.becomeLeader()
			return
		}
		for i := range sm.ins {
			if i == sm.addr {
				continue
			}
			lasti := sm.log.lastIndex()
			sm.send(Message{To: i, Type: msgVote, Index: lasti, LogTerm: sm.log.term(lasti)})
		}
		return
	case msgBeat:
		if sm.state != stateLeader {
			return
		}
		sm.bcastAppend()
		return
	case msgProp:
		if len(m.Entries) != 1 {
			panic("unexpected length(entries) of a msgProp")
		}

		switch sm.lead {
		case sm.addr:
			e := m.Entries[0]
			if e.Type == config {
				if sm.pendingConf {
					// todo: deny
					return
				}
				sm.pendingConf = true
			}
			e.Term = sm.term

			sm.log.append(sm.log.lastIndex(), e)
			sm.ins[sm.addr].update(sm.log.lastIndex())
			sm.maybeCommit()
			sm.bcastAppend()
		case none:
			panic("msgProp given without leader")
		default:
			m.To = sm.lead
			sm.send(m)
		}
		return
	}

	switch {
	case m.Term > sm.term:
		sm.becomeFollower(m.Term, m.From)
	case m.Term < sm.term:
		// ignore
		return
	}

	handleAppendEntries := func() {
		if sm.log.maybeAppend(m.Index, m.LogTerm, m.Commit, m.Entries...) {
			sm.send(Message{To: m.From, Type: msgAppResp, Index: sm.log.lastIndex()})
		} else {
			sm.send(Message{To: m.From, Type: msgAppResp, Index: -1})
		}
	}

	switch sm.state {
	case stateLeader:
		switch m.Type {
		case msgAppResp:
			if m.Index < 0 {
				sm.ins[m.From].decr()
				sm.sendAppend(m.From)
			} else {
				sm.ins[m.From].update(m.Index)
				if sm.maybeCommit() {
					sm.bcastAppend()
				}
			}
		case msgVote:
			sm.send(Message{To: m.From, Type: msgVoteResp, Index: -1})
		}
	case stateCandidate:
		switch m.Type {
		case msgApp:
			sm.becomeFollower(sm.term, m.From)
			handleAppendEntries()
		case msgVote:
			sm.send(Message{To: m.From, Type: msgVoteResp, Index: -1})
		case msgVoteResp:
			gr := sm.poll(m.From, m.Index >= 0)
			switch sm.q() {
			case gr:
				sm.becomeLeader()
				sm.bcastAppend()
			case len(sm.votes) - gr:
				sm.becomeFollower(sm.term, none)
			}
		}
	case stateFollower:
		switch m.Type {
		case msgApp:
			handleAppendEntries()
		case msgVote:
			if (sm.vote == none || sm.vote == m.From) && sm.log.isUpToDate(m.Index, m.LogTerm) {
				sm.vote = m.From
				sm.send(Message{To: m.From, Type: msgVoteResp, Index: sm.log.lastIndex()})
			} else {
				sm.send(Message{To: m.From, Type: msgVoteResp, Index: -1})
			}
		}
	}
}
<|endoftext|>


<commit_before>package rake

/*
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

import (
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"unicode"
)

var wordSep *regexp.Regexp = regexp.MustCompile("\\s+")
var wordSplitter *regexp.Regexp = regexp.MustCompile("[^\\p{L}0-9\\+\\-/]")

func sentenceSplitter(r rune) bool {
	// Ignore hyphens, usually they are used to join words (e.g., "ice-cream-flavored candy")
	return unicode.IsPunct(r) && !unicode.Is(unicode.Properties["Hyphen"], r)
}

func IsAcceptable(phrase string, minCharLength int, maxWordsLength int) bool {
	// a phrase must have a min length in characters
	if len(phrase) < minCharLength {
		return false
	}

	// a phrase must have a max number of words
	words := wordSep.Split(phrase, -1)
	if len(words) > maxWordsLength {
		return false
	}
	digits := 0
	alpha := 0
	for _, c := range phrase {
		if unicode.IsDigit(c) {
			digits++
		} else if unicode.IsLetter(c) {
			alpha++
		}
	}
	// a phrase must have at least one alpha character
	if alpha == 0 {
		return false
	}
	// a phrase must have more alpha than digits characters
	if digits > alpha {
		return false
	}
	return true
}

func findStopwordIndices(words []string, stopwords map[string]bool) []int {
	result := []int{}
	if stopwords != nil {
		for i, word := range words {
			if stopwords[strings.ToLower(word)] {
				result = append(result, i)
			}
		}
	}
	return result
}

func GenerateCandidateKeywords(sentenceList []string, stopwords map[string]bool, minCharLength int, maxWordsLength int) []string {
	phraseList := []string{}
	for _, s := range sentenceList {
		words := strings.Fields(s)
		stopwordIndices := findStopwordIndices(words, stopwords)

		if len(stopwordIndices) > 0 {
			if stopwordIndices[0] != 0 {
				phraseWords := words[0:stopwordIndices[0]]
				phrase := strings.Join(phraseWords, " ")
				if phrase != "" && IsAcceptable(phrase, minCharLength, maxWordsLength) {
					phraseList = append(phraseList, phrase)
				}
			}
			for i, index := range stopwordIndices {
				j := i + 1
				if j < len(stopwordIndices) {
					index2 := stopwordIndices[j]
					if index2-index == 1 {
						continue
					}
					phraseWords := words[index+1 : index2]
					phrase := strings.Join(phraseWords, " ")
					if phrase != "" && IsAcceptable(phrase, minCharLength, maxWordsLength) {
						phraseList = append(phraseList, phrase)
					}
				}
			}
			if stopwordIndices[len(stopwordIndices)-1] != len(words)-1 {
				index := stopwordIndices[len(stopwordIndices)-1]
				phraseWords := words[index+1:]
				phrase := strings.Join(phraseWords, " ")
				if phrase != "" && IsAcceptable(phrase, minCharLength, maxWordsLength) {
					phraseList = append(phraseList, phrase)
				}
			}
		} else {
			phrase := strings.Join(words, " ")
			if phrase != "" && IsAcceptable(phrase, minCharLength, maxWordsLength) {
				phraseList = append(phraseList, phrase)
			}
		}
	}
	return phraseList
}

func IsNumber(s string) bool {
	if strings.Index(s, ".") != -1 {
		_, err := strconv.ParseFloat(s, 32)
		return err == nil
	} else {
		_, err := strconv.ParseInt(s, 10, 32)
		return err == nil
	}
}

func SeparateWords(text string, minWordReturnSize int) []string {
	//    Utility function to return a list of all words that are have a length
	//    greater than a specified number of characters.
	//    @param text The text that must be split in to words.
	//    @param min_word_return_size The minimum no of characters
	//           a word must have to be included.
	words := []string{}
	for _, singleWord := range wordSplitter.Split(text, -1) {
		currentWord := strings.ToLower(strings.TrimSpace(singleWord))
		// leave numbers in phrase, but don't count as words, since they tend to invalidate scores of their phrases
		if len(currentWord) > minWordReturnSize && currentWord != "" && !IsNumber(currentWord) {
			words = append(words, currentWord)
		}
	}
	return words
}

func CalculateWordScores(phraseList []string) map[string]float64 {
	wordFrequency := map[string]int{}
	wordDegree := map[string]int{}
	for _, phrase := range phraseList {
		wordList := SeparateWords(phrase, 0)
		wordListLength := len(wordList)
		wordListDegree := wordListLength - 1
		// if word_list_degree > 3: word_list_degree = 3 #exp.
		for _, word := range wordList {
			wordFrequency[word]++
			wordDegree[word] += wordListDegree
		}
	}
	for item := range wordFrequency {
		wordDegree[item] = wordDegree[item] + wordFrequency[item]
	}
	// Calculate Word scores = deg(w)/frew(w)
	wordScore := map[string]float64{}
	for item := range wordFrequency {
		wordScore[item] = float64(wordDegree[item]) / float64(wordFrequency[item])
	}
	return wordScore
}

func StringCount(stringList []string) map[string]int {
	stringCount := map[string]int{}
	for _, item := range stringList {
		stringCount[item]++
	}
	return stringCount
}

func GenerateCandidateKeywordScores(phraseList []string, wordScore map[string]float64, minKeywordFrequency int) (map[string]float64, map[string]int) {
	keywordCandidates := map[string]float64{}
	phraseCounts := StringCount(phraseList)
	for _, phrase := range phraseList {
		if minKeywordFrequency > 1 {
			if phraseCounts[phrase] < minKeywordFrequency {
				continue
			}
		}
		wordList := SeparateWords(phrase, 0)
		var candiateScore float64 = 0
		for _, word := range wordList {
			candiateScore += wordScore[word]
		}
		keywordCandidates[phrase] = candiateScore
	}

	return keywordCandidates, phraseCounts
}

func MapToKeywordScores(keywordScoreMap map[string]float64, keywordCountMap map[string]int) []KeywordScore {
	keywordScores := []KeywordScore{}
	for key, value := range keywordScoreMap {
		keywordScore := KeywordScore{
			Keyword: key,
			Score:   value,
		}
		count, _ := keywordCountMap[key]
		keywordScore.Count = count
		keywordScores = append(keywordScores, keywordScore)
	}
	return keywordScores
}

func loadStopWords(stopWordFile string) []string {
	stopWords := []string{}
	contents, err := ioutil.ReadFile(stopWordFile)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	lines := strings.Split(string(contents), "\n")
	for _, line := range lines {
		line = strings.ToLower(strings.TrimSpace(line))
		if strings.Index(line, "#") != 0 {
			// in case more than one per line
			for _, word := range strings.Fields(line) {
				stopWords = append(stopWords, word)
			}

		}
	}
	return stopWords
}

func BuildStopWordMap(stopWordList []string) map[string]bool {
	stopWordMap := make(map[string]bool, len(stopWordList))
	for _, word := range stopWordList {
		stopWordMap[strings.ToLower(word)] = true
	}
	return stopWordMap
}

// ByScore implements sort.Interface for []KeywordScore based on
// the Score field. Sorts in reverse order.
type ByScore []KeywordScore

func (a ByScore) Len() int           { return len(a) }
func (a ByScore) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByScore) Less(i, j int) bool { return a[i].Score > a[j].Score }

type Rake struct {
	stopWordsPath       string
	stopWords           map[string]bool
	minCharLength       int
	maxWordsLength      int
	minKeywordFrequency int
}

type KeywordScore struct {
	Keyword string
	Score   float64
	Count   int
}

func NewRake(stopWordsPath string, minCharLength int, maxWordsLength int, minKeywordFrequency int) *Rake {
	rake := &Rake{
		minCharLength:       minCharLength,
		maxWordsLength:      maxWordsLength,
		minKeywordFrequency: minKeywordFrequency,
	}

	if stopWordsPath != "" {
		rake.stopWords = BuildStopWordMap(loadStopWords(stopWordsPath))
	}

	return rake
}

func (rake *Rake) SetStopWords(stopWordsList []string) {
	rake.stopWords = BuildStopWordMap(stopWordsList)
}

func (rake *Rake) Run(text string) []KeywordScore {
	sentenceList := strings.FieldsFunc(text, sentenceSplitter)
	phraseList := GenerateCandidateKeywords(sentenceList, rake.stopWords, rake.minCharLength, rake.maxWordsLength)
	wordScores := CalculateWordScores(phraseList)
	keywordCandidates, keywordCounts := GenerateCandidateKeywordScores(phraseList, wordScores, rake.minKeywordFrequency)
	keywordScores := MapToKeywordScores(keywordCandidates, keywordCounts)
	sort.Sort(ByScore(keywordScores))
	return keywordScores
}
<commit_msg>Do not match any kind of numeric character in any script in wordSplitter<commit_after>package rake

/*
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

import (
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"unicode"
)

var wordSep *regexp.Regexp = regexp.MustCompile("\\s+")
var wordSplitter *regexp.Regexp = regexp.MustCompile("[^\\p{L}\\p{N}\\+\\-/]")

func sentenceSplitter(r rune) bool {
	// Ignore hyphens, usually they are used to join words (e.g., "ice-cream-flavored candy")
	return unicode.IsPunct(r) && !unicode.Is(unicode.Properties["Hyphen"], r)
}

func IsAcceptable(phrase string, minCharLength int, maxWordsLength int) bool {
	// a phrase must have a min length in characters
	if len(phrase) < minCharLength {
		return false
	}

	// a phrase must have a max number of words
	words := wordSep.Split(phrase, -1)
	if len(words) > maxWordsLength {
		return false
	}
	digits := 0
	alpha := 0
	for _, c := range phrase {
		if unicode.IsDigit(c) {
			digits++
		} else if unicode.IsLetter(c) {
			alpha++
		}
	}
	// a phrase must have at least one alpha character
	if alpha == 0 {
		return false
	}
	// a phrase must have more alpha than digits characters
	if digits > alpha {
		return false
	}
	return true
}

func findStopwordIndices(words []string, stopwords map[string]bool) []int {
	result := []int{}
	if stopwords != nil {
		for i, word := range words {
			if stopwords[strings.ToLower(word)] {
				result = append(result, i)
			}
		}
	}
	return result
}

func GenerateCandidateKeywords(sentenceList []string, stopwords map[string]bool, minCharLength int, maxWordsLength int) []string {
	phraseList := []string{}
	for _, s := range sentenceList {
		words := strings.Fields(s)
		stopwordIndices := findStopwordIndices(words, stopwords)

		if len(stopwordIndices) > 0 {
			if stopwordIndices[0] != 0 {
				phraseWords := words[0:stopwordIndices[0]]
				phrase := strings.Join(phraseWords, " ")
				if phrase != "" && IsAcceptable(phrase, minCharLength, maxWordsLength) {
					phraseList = append(phraseList, phrase)
				}
			}
			for i, index := range stopwordIndices {
				j := i + 1
				if j < len(stopwordIndices) {
					index2 := stopwordIndices[j]
					if index2-index == 1 {
						continue
					}
					phraseWords := words[index+1 : index2]
					phrase := strings.Join(phraseWords, " ")
					if phrase != "" && IsAcceptable(phrase, minCharLength, maxWordsLength) {
						phraseList = append(phraseList, phrase)
					}
				}
			}
			if stopwordIndices[len(stopwordIndices)-1] != len(words)-1 {
				index := stopwordIndices[len(stopwordIndices)-1]
				phraseWords := words[index+1:]
				phrase := strings.Join(phraseWords, " ")
				if phrase != "" && IsAcceptable(phrase, minCharLength, maxWordsLength) {
					phraseList = append(phraseList, phrase)
				}
			}
		} else {
			phrase := strings.Join(words, " ")
			if phrase != "" && IsAcceptable(phrase, minCharLength, maxWordsLength) {
				phraseList = append(phraseList, phrase)
			}
		}
	}
	return phraseList
}

func IsNumber(s string) bool {
	if strings.Index(s, ".") != -1 {
		_, err := strconv.ParseFloat(s, 32)
		return err == nil
	} else {
		_, err := strconv.ParseInt(s, 10, 32)
		return err == nil
	}
}

func SeparateWords(text string, minWordReturnSize int) []string {
	//    Utility function to return a list of all words that are have a length
	//    greater than a specified number of characters.
	//    @param text The text that must be split in to words.
	//    @param min_word_return_size The minimum no of characters
	//           a word must have to be included.
	words := []string{}
	for _, singleWord := range wordSplitter.Split(text, -1) {
		currentWord := strings.ToLower(strings.TrimSpace(singleWord))
		// leave numbers in phrase, but don't count as words, since they tend to invalidate scores of their phrases
		if len(currentWord) > minWordReturnSize && currentWord != "" && !IsNumber(currentWord) {
			words = append(words, currentWord)
		}
	}
	return words
}

func CalculateWordScores(phraseList []string) map[string]float64 {
	wordFrequency := map[string]int{}
	wordDegree := map[string]int{}
	for _, phrase := range phraseList {
		wordList := SeparateWords(phrase, 0)
		wordListLength := len(wordList)
		wordListDegree := wordListLength - 1
		// if word_list_degree > 3: word_list_degree = 3 #exp.
		for _, word := range wordList {
			wordFrequency[word]++
			wordDegree[word] += wordListDegree
		}
	}
	for item := range wordFrequency {
		wordDegree[item] = wordDegree[item] + wordFrequency[item]
	}
	// Calculate Word scores = deg(w)/frew(w)
	wordScore := map[string]float64{}
	for item := range wordFrequency {
		wordScore[item] = float64(wordDegree[item]) / float64(wordFrequency[item])
	}
	return wordScore
}

func StringCount(stringList []string) map[string]int {
	stringCount := map[string]int{}
	for _, item := range stringList {
		stringCount[item]++
	}
	return stringCount
}

func GenerateCandidateKeywordScores(phraseList []string, wordScore map[string]float64, minKeywordFrequency int) (map[string]float64, map[string]int) {
	keywordCandidates := map[string]float64{}
	phraseCounts := StringCount(phraseList)
	for _, phrase := range phraseList {
		if minKeywordFrequency > 1 {
			if phraseCounts[phrase] < minKeywordFrequency {
				continue
			}
		}
		wordList := SeparateWords(phrase, 0)
		var candiateScore float64 = 0
		for _, word := range wordList {
			candiateScore += wordScore[word]
		}
		keywordCandidates[phrase] = candiateScore
	}

	return keywordCandidates, phraseCounts
}

func MapToKeywordScores(keywordScoreMap map[string]float64, keywordCountMap map[string]int) []KeywordScore {
	keywordScores := []KeywordScore{}
	for key, value := range keywordScoreMap {
		keywordScore := KeywordScore{
			Keyword: key,
			Score:   value,
		}
		count, _ := keywordCountMap[key]
		keywordScore.Count = count
		keywordScores = append(keywordScores, keywordScore)
	}
	return keywordScores
}

func loadStopWords(stopWordFile string) []string {
	stopWords := []string{}
	contents, err := ioutil.ReadFile(stopWordFile)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	lines := strings.Split(string(contents), "\n")
	for _, line := range lines {
		line = strings.ToLower(strings.TrimSpace(line))
		if strings.Index(line, "#") != 0 {
			// in case more than one per line
			for _, word := range strings.Fields(line) {
				stopWords = append(stopWords, word)
			}

		}
	}
	return stopWords
}

func BuildStopWordMap(stopWordList []string) map[string]bool {
	stopWordMap := make(map[string]bool, len(stopWordList))
	for _, word := range stopWordList {
		stopWordMap[strings.ToLower(word)] = true
	}
	return stopWordMap
}

// ByScore implements sort.Interface for []KeywordScore based on
// the Score field. Sorts in reverse order.
type ByScore []KeywordScore

func (a ByScore) Len() int           { return len(a) }
func (a ByScore) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByScore) Less(i, j int) bool { return a[i].Score > a[j].Score }

type Rake struct {
	stopWordsPath       string
	stopWords           map[string]bool
	minCharLength       int
	maxWordsLength      int
	minKeywordFrequency int
}

type KeywordScore struct {
	Keyword string
	Score   float64
	Count   int
}

func NewRake(stopWordsPath string, minCharLength int, maxWordsLength int, minKeywordFrequency int) *Rake {
	rake := &Rake{
		minCharLength:       minCharLength,
		maxWordsLength:      maxWordsLength,
		minKeywordFrequency: minKeywordFrequency,
	}

	if stopWordsPath != "" {
		rake.stopWords = BuildStopWordMap(loadStopWords(stopWordsPath))
	}

	return rake
}

func (rake *Rake) SetStopWords(stopWordsList []string) {
	rake.stopWords = BuildStopWordMap(stopWordsList)
}

func (rake *Rake) Run(text string) []KeywordScore {
	sentenceList := strings.FieldsFunc(text, sentenceSplitter)
	phraseList := GenerateCandidateKeywords(sentenceList, rake.stopWords, rake.minCharLength, rake.maxWordsLength)
	wordScores := CalculateWordScores(phraseList)
	keywordCandidates, keywordCounts := GenerateCandidateKeywordScores(phraseList, wordScores, rake.minKeywordFrequency)
	keywordScores := MapToKeywordScores(keywordCandidates, keywordCounts)
	sort.Sort(ByScore(keywordScores))
	return keywordScores
}
<|endoftext|>


<commit_before>package pubsub

import (
	"encoding/json"
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/websocket"
)

type connection struct {
	ws       *websocket.Conn
	channels []string
	Send     chan Parcel
	done     chan bool
}

type wsPayload struct {
	Action  string           `json:"action,omitempty"`
	Channel string           `json:"channel"`
	Data    *json.RawMessage `json:"data,omitempty"`
}

// WsPubSub is a websocket trsnaport of pubsub
// Protocol: {"action": "sub", "channel": "royuen"}
// {"action": "pub", "channel": "royuen", "data": {"any":"thing"}}
type WsPubSub struct {
	upgrader websocket.Upgrader
	hub      *Hub
}

// NewWsPubsub is factory for WsPubSub
func NewWsPubsub(hub *Hub) *WsPubSub {
	if hub == nil {
		hub = NewHub()
	}
	upgrader := websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		CheckOrigin: func(r *http.Request) bool {
			// allow all connections
			return true
		},
	}
	ws := WsPubSub{
		upgrader,
		hub,
	}
	go hub.run()
	return &ws
}

// Handle will hijack the http responseWriter and req.
func (w *WsPubSub) Handle(writer http.ResponseWriter, req *http.Request) {
	conn, err := w.upgrader.Upgrade(writer, req, nil)
	if err != nil {
		log.Println(err)
		return
	}
	c := &connection{
		ws:   conn,
		Send: make(chan Parcel),
		done: make(chan bool),
	}
	go w.writer(c)
	go w.reader(c)
}

func (w *WsPubSub) writer(c *connection) {
writer:
	for {
		select {
		case parcel := <-c.Send:
			log.Debugf("Writing ws %p, %s, %s", c.ws, parcel.Channel, parcel.Data)
			d := json.RawMessage(parcel.Data)
			message, _ := json.Marshal(wsPayload{
				Channel: parcel.Channel,
				Data:    &d,
			})
			c.ws.WriteMessage(websocket.TextMessage, message)
		case <-c.done:
			break writer
		}
	}
	log.Debugf("Close ws writer goroutine %p", c.ws)
}

func (w *WsPubSub) reader(c *connection) {
	defer func() {
		log.Debugf("Close ws writer connection %p", c.ws)
		c.ws.Close()
		for _, channel := range c.channels {
			w.hub.Unsubscribe <- Parcel{
				Channel:    channel,
				Connection: c,
			}
		}
		c.done <- true
	}()
	for {
		log.Debugf("Waiting ws message %p", c.ws)
		messageType, p, err := c.ws.ReadMessage()
		if err != nil {
			log.Debugf("Ws error %v", err)
			return
		}
		log.Debugf("Received %v, %s", messageType, p)
		var payload wsPayload
		err = json.Unmarshal(p, &payload)
		if err != nil {
			log.Debugf("Can't decode Ws message %v", err)
			c.ws.WriteMessage(
				websocket.TextMessage,
				[]byte("Error: "+err.Error()+" \nClosing Connection"))
			c.ws.WriteMessage(websocket.CloseMessage, nil)
			return
		}
		switch payload.Action {
		case "sub":
			w.hub.Subscribe <- Parcel{
				Channel:    payload.Channel,
				Connection: c,
			}
			c.channels = append(c.channels, payload.Channel)
		case "unsub":
			w.hub.Unsubscribe <- Parcel{
				Channel:    payload.Channel,
				Connection: c,
			}
			c.channels = append(c.channels, payload.Channel)
		case "pub":
			w.hub.Broadcast <- Parcel{
				Channel: payload.Channel,
				Data:    []byte(*payload.Data),
			}
		default:
			c.ws.WriteMessage(
				websocket.TextMessage,
				[]byte("Unknow action"))
		}
	}
}
<commit_msg>Fix typos on close ws reader log<commit_after>package pubsub

import (
	"encoding/json"
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/websocket"
)

type connection struct {
	ws       *websocket.Conn
	channels []string
	Send     chan Parcel
	done     chan bool
}

type wsPayload struct {
	Action  string           `json:"action,omitempty"`
	Channel string           `json:"channel"`
	Data    *json.RawMessage `json:"data,omitempty"`
}

// WsPubSub is a websocket trsnaport of pubsub
// Protocol: {"action": "sub", "channel": "royuen"}
// {"action": "pub", "channel": "royuen", "data": {"any":"thing"}}
type WsPubSub struct {
	upgrader websocket.Upgrader
	hub      *Hub
}

// NewWsPubsub is factory for WsPubSub
func NewWsPubsub(hub *Hub) *WsPubSub {
	if hub == nil {
		hub = NewHub()
	}
	upgrader := websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		CheckOrigin: func(r *http.Request) bool {
			// allow all connections
			return true
		},
	}
	ws := WsPubSub{
		upgrader,
		hub,
	}
	go hub.run()
	return &ws
}

// Handle will hijack the http responseWriter and req.
func (w *WsPubSub) Handle(writer http.ResponseWriter, req *http.Request) {
	conn, err := w.upgrader.Upgrade(writer, req, nil)
	if err != nil {
		log.Println(err)
		return
	}
	c := &connection{
		ws:   conn,
		Send: make(chan Parcel),
		done: make(chan bool),
	}
	go w.writer(c)
	go w.reader(c)
}

func (w *WsPubSub) writer(c *connection) {
writer:
	for {
		select {
		case parcel := <-c.Send:
			log.Debugf("Writing ws %p, %s, %s", c.ws, parcel.Channel, parcel.Data)
			d := json.RawMessage(parcel.Data)
			message, _ := json.Marshal(wsPayload{
				Channel: parcel.Channel,
				Data:    &d,
			})
			c.ws.WriteMessage(websocket.TextMessage, message)
		case <-c.done:
			break writer
		}
	}
	log.Debugf("Close ws writer goroutine %p", c.ws)
}

func (w *WsPubSub) reader(c *connection) {
	defer func() {
		log.Debugf("Close ws reader connection %p", c.ws)
		c.ws.Close()
		for _, channel := range c.channels {
			w.hub.Unsubscribe <- Parcel{
				Channel:    channel,
				Connection: c,
			}
		}
		c.done <- true
	}()
	for {
		log.Debugf("Waiting ws message %p", c.ws)
		messageType, p, err := c.ws.ReadMessage()
		if err != nil {
			log.Debugf("Ws error %v", err)
			return
		}
		log.Debugf("Received %v, %s", messageType, p)
		var payload wsPayload
		err = json.Unmarshal(p, &payload)
		if err != nil {
			log.Debugf("Can't decode Ws message %v", err)
			c.ws.WriteMessage(
				websocket.TextMessage,
				[]byte("Error: "+err.Error()+" \nClosing Connection"))
			c.ws.WriteMessage(websocket.CloseMessage, nil)
			return
		}
		switch payload.Action {
		case "sub":
			w.hub.Subscribe <- Parcel{
				Channel:    payload.Channel,
				Connection: c,
			}
			c.channels = append(c.channels, payload.Channel)
		case "unsub":
			w.hub.Unsubscribe <- Parcel{
				Channel:    payload.Channel,
				Connection: c,
			}
			c.channels = append(c.channels, payload.Channel)
		case "pub":
			w.hub.Broadcast <- Parcel{
				Channel: payload.Channel,
				Data:    []byte(*payload.Data),
			}
		default:
			c.ws.WriteMessage(
				websocket.TextMessage,
				[]byte("Unknow action"))
		}
	}
}
<|endoftext|>


<commit_before>// +build linux

package raw

import (
	"net"
	"sync"
	"syscall"
	"time"

	"golang.org/x/net/context"
)

var (
	// Must implement net.PacketConn at compile-time.
	_ net.PacketConn = &packetConn{}
)

// packetConn is the Linux-specific implementation of net.PacketConn for this
// package.
type packetConn struct {
	ifi *net.Interface
	s   socket

	// Sleep function implementation
	sleeper sleeper

	// Timeouts set via Set{Read,Write,}Deadline, guarded by mutex
	timeoutMu sync.RWMutex
	rtimeout  time.Time
	wtimeout  time.Time
}

// socket is an interface which enables swapping out socket syscalls for
// testing.
type socket interface {
	Bind(syscall.Sockaddr) error
	Close() error
	Recvfrom([]byte, int) (int, syscall.Sockaddr, error)
	Sendto([]byte, int, syscall.Sockaddr) error
	SetNonblock(bool) error
}

// sleeper is an interface which enables swapping out an actual time.Sleep
// call for testing.
type sleeper interface {
	Sleep(time.Duration)
}

// listenPacket creates a net.PacketConn which can be used to send and receive
// data at the device driver level.
//
// ifi specifies the network interface which will be used to send and receive
// data.  socket specifies the socket type to be used, such as syscall.SOCK_RAW
// or syscall.SOCK_DGRAM.  proto specifies the protocol which should be
// captured and transmitted.  proto is automatically converted to network byte
// order (big endian), akin to the htons() function in C.
func listenPacket(ifi *net.Interface, socket int, proto int) (*packetConn, error) {
	// Convert proto to big endian
	pbe := htons(uint16(proto))

	// Open a packet socket using specified socket and protocol types
	sock, err := syscall.Socket(syscall.AF_PACKET, socket, int(pbe))
	if err != nil {
		return nil, err
	}

	// Wrap raw socket in socket interface, use actual time package sleeper
	return newPacketConn(
		ifi,
		&sysSocket{
			fd: sock,
		},
		pbe,
		&timeSleeper{},
	)
}

// newPacketConn creates a net.PacketConn using the specified network
// interface, wrapped socket, big endian protocol number, and Sleep
// implementation used for read/write retries.
//
// It is the entry point for tests in this package.
func newPacketConn(ifi *net.Interface, s socket, pbe uint16, sleeper sleeper) (*packetConn, error) {
	// Set nonblocking I/O so we can time out reads and writes
	if err := s.SetNonblock(true); err != nil {
		return nil, err
	}

	// Bind the packet socket to the interface specified by ifi
	// packet(7):
	//   Only the sll_protocol and the sll_ifindex address fields are used for
	//   purposes of binding.
	err := s.Bind(&syscall.SockaddrLinklayer{
		Protocol: pbe,
		Ifindex:  ifi.Index,
	})

	return &packetConn{
		ifi:     ifi,
		s:       s,
		sleeper: sleeper,
	}, err
}

// ReadFrom implements the net.PacketConn.ReadFrom method.
func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) {
	// Set up deadline context if needed, if a read timeout is set
	ctx, cancel := context.TODO(), func() {}
	p.timeoutMu.RLock()
	if p.rtimeout.After(time.Now()) {
		ctx, cancel = context.WithDeadline(context.Background(), p.rtimeout)
	}
	p.timeoutMu.RUnlock()

	// Information returned by syscall.Recvfrom
	var n int
	var addr syscall.Sockaddr
	var err error

	for {
		// Continue looping, or if deadline is set and has expired, return
		// an error
		select {
		case <-ctx.Done():
			// We only know how to handle deadline exceeded, so return any
			// other errors for the caller to deal with
			if err := ctx.Err(); err != context.DeadlineExceeded {
				return n, nil, err
			}

			// Return standard net.OpError so caller can detect timeouts and retry
			return n, nil, &net.OpError{
				Op:   "read",
				Net:  "raw",
				Addr: nil,
				Err:  &timeoutError{},
			}
		default:
			// Not timed out, keep trying
		}

		// Attempt to receive on socket
		n, addr, err = p.s.Recvfrom(b, 0)
		if err != nil {
			n = 0

			// EAGAIN is returned when no data is available for non-blocking
			// I/O, so keep trying after a short delay
			if err == syscall.EAGAIN {
				p.sleeper.Sleep(2 * time.Millisecond)
				continue
			}

			// Return other errors
			return n, nil, err
		}

		// Got data, cancel the deadline
		cancel()
		break
	}

	// Retrieve hardware address and other information from addr
	sa, ok := addr.(*syscall.SockaddrLinklayer)
	if !ok || sa.Halen < 6 {
		return n, nil, syscall.EINVAL
	}

	// Use length specified to convert byte array into a hardware address slice
	mac := make(net.HardwareAddr, sa.Halen)
	copy(mac, sa.Addr[:])

	// packet(7):
	//   sll_hatype and sll_pkttype are set on received packets for your
	//   information.
	// TODO(mdlayher): determine if similar fields exist and are useful on
	// non-Linux platforms
	return n, &Addr{
		HardwareAddr: mac,
	}, nil
}

// WriteTo implements the net.PacketConn.WriteTo method.
//
// TODO(mdlayher): implement write deadlines
func (p *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) {
	// Ensure correct Addr type
	a, ok := addr.(*Addr)
	if !ok {
		return 0, syscall.EINVAL
	}

	// Convert hardware address back to byte array form
	var baddr [8]byte
	copy(baddr[:], a.HardwareAddr)

	// Send message on socket to the specified hardware address from addr
	// packet(7):
	//   When you send packets it is enough to specify sll_family, sll_addr,
	//   sll_halen, sll_ifindex.  The other fields should  be 0.
	// In this case, sll_family is taken care of automatically by syscall
	err := p.s.Sendto(b, 0, &syscall.SockaddrLinklayer{
		Ifindex: p.ifi.Index,
		Halen:   uint8(len(a.HardwareAddr)),
		Addr:    baddr,
	})
	return len(b), err
}

// Close closes the connection.
func (p *packetConn) Close() error {
	return p.s.Close()
}

// LocalAddr returns the local network address.
func (p *packetConn) LocalAddr() net.Addr {
	return &Addr{
		HardwareAddr: p.ifi.HardwareAddr,
	}
}

// TODO(mdlayher): it is unfortunate that we have to implement deadlines using
// a context, but it appears that there may not be a better solution until
// Go 1.6 or later.  See here: https://github.com/golang/go/issues/10565.

// SetDeadline implements the net.PacketConn.SetDeadline method.
func (p *packetConn) SetDeadline(t time.Time) error {
	p.timeoutMu.Lock()
	p.rtimeout = t
	p.wtimeout = t
	p.timeoutMu.Unlock()

	return nil
}

// SetReadDeadline implements the net.PacketConn.SetReadDeadline method.
func (p *packetConn) SetReadDeadline(t time.Time) error {
	p.timeoutMu.Lock()
	p.rtimeout = t
	p.timeoutMu.Unlock()

	return nil
}

// SetWriteDeadline implements the net.PacketConn.SetWriteDeadline method.
func (p *packetConn) SetWriteDeadline(t time.Time) error {
	return ErrNotImplemented
}

// sysSocket is the default socket implementation.  It makes use of
// Linux-specific system calls to handle raw socket functionality.
type sysSocket struct {
	fd int
}

// Method implementations simply invoke the syscall of the same name, but pass
// the file descriptor stored in the sysSocket as the socket to use.
func (s *sysSocket) Bind(sa syscall.Sockaddr) error { return syscall.Bind(s.fd, sa) }
func (s *sysSocket) Close() error                   { return syscall.Close(s.fd) }
func (s *sysSocket) Recvfrom(p []byte, flags int) (int, syscall.Sockaddr, error) {
	return syscall.Recvfrom(s.fd, p, flags)
}
func (s *sysSocket) Sendto(p []byte, flags int, to syscall.Sockaddr) error {
	return syscall.Sendto(s.fd, p, flags, to)
}
func (s *sysSocket) SetNonblock(nonblocking bool) error { return syscall.SetNonblock(s.fd, nonblocking) }

// timeSleeper sleeps using time.Sleep.
type timeSleeper struct{}

func (_ timeSleeper) Sleep(d time.Duration) {
	time.Sleep(d)
}
<commit_msg>raw_linux: check for short hardware address during WriteTo<commit_after>// +build linux

package raw

import (
	"net"
	"sync"
	"syscall"
	"time"

	"golang.org/x/net/context"
)

var (
	// Must implement net.PacketConn at compile-time.
	_ net.PacketConn = &packetConn{}
)

// packetConn is the Linux-specific implementation of net.PacketConn for this
// package.
type packetConn struct {
	ifi *net.Interface
	s   socket

	// Sleep function implementation
	sleeper sleeper

	// Timeouts set via Set{Read,Write,}Deadline, guarded by mutex
	timeoutMu sync.RWMutex
	rtimeout  time.Time
	wtimeout  time.Time
}

// socket is an interface which enables swapping out socket syscalls for
// testing.
type socket interface {
	Bind(syscall.Sockaddr) error
	Close() error
	Recvfrom([]byte, int) (int, syscall.Sockaddr, error)
	Sendto([]byte, int, syscall.Sockaddr) error
	SetNonblock(bool) error
}

// sleeper is an interface which enables swapping out an actual time.Sleep
// call for testing.
type sleeper interface {
	Sleep(time.Duration)
}

// listenPacket creates a net.PacketConn which can be used to send and receive
// data at the device driver level.
//
// ifi specifies the network interface which will be used to send and receive
// data.  socket specifies the socket type to be used, such as syscall.SOCK_RAW
// or syscall.SOCK_DGRAM.  proto specifies the protocol which should be
// captured and transmitted.  proto is automatically converted to network byte
// order (big endian), akin to the htons() function in C.
func listenPacket(ifi *net.Interface, socket int, proto int) (*packetConn, error) {
	// Convert proto to big endian
	pbe := htons(uint16(proto))

	// Open a packet socket using specified socket and protocol types
	sock, err := syscall.Socket(syscall.AF_PACKET, socket, int(pbe))
	if err != nil {
		return nil, err
	}

	// Wrap raw socket in socket interface, use actual time package sleeper
	return newPacketConn(
		ifi,
		&sysSocket{
			fd: sock,
		},
		pbe,
		&timeSleeper{},
	)
}

// newPacketConn creates a net.PacketConn using the specified network
// interface, wrapped socket, big endian protocol number, and Sleep
// implementation used for read/write retries.
//
// It is the entry point for tests in this package.
func newPacketConn(ifi *net.Interface, s socket, pbe uint16, sleeper sleeper) (*packetConn, error) {
	// Set nonblocking I/O so we can time out reads and writes
	if err := s.SetNonblock(true); err != nil {
		return nil, err
	}

	// Bind the packet socket to the interface specified by ifi
	// packet(7):
	//   Only the sll_protocol and the sll_ifindex address fields are used for
	//   purposes of binding.
	err := s.Bind(&syscall.SockaddrLinklayer{
		Protocol: pbe,
		Ifindex:  ifi.Index,
	})

	return &packetConn{
		ifi:     ifi,
		s:       s,
		sleeper: sleeper,
	}, err
}

// ReadFrom implements the net.PacketConn.ReadFrom method.
func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) {
	// Set up deadline context if needed, if a read timeout is set
	ctx, cancel := context.TODO(), func() {}
	p.timeoutMu.RLock()
	if p.rtimeout.After(time.Now()) {
		ctx, cancel = context.WithDeadline(context.Background(), p.rtimeout)
	}
	p.timeoutMu.RUnlock()

	// Information returned by syscall.Recvfrom
	var n int
	var addr syscall.Sockaddr
	var err error

	for {
		// Continue looping, or if deadline is set and has expired, return
		// an error
		select {
		case <-ctx.Done():
			// We only know how to handle deadline exceeded, so return any
			// other errors for the caller to deal with
			if err := ctx.Err(); err != context.DeadlineExceeded {
				return n, nil, err
			}

			// Return standard net.OpError so caller can detect timeouts and retry
			return n, nil, &net.OpError{
				Op:   "read",
				Net:  "raw",
				Addr: nil,
				Err:  &timeoutError{},
			}
		default:
			// Not timed out, keep trying
		}

		// Attempt to receive on socket
		n, addr, err = p.s.Recvfrom(b, 0)
		if err != nil {
			n = 0

			// EAGAIN is returned when no data is available for non-blocking
			// I/O, so keep trying after a short delay
			if err == syscall.EAGAIN {
				p.sleeper.Sleep(2 * time.Millisecond)
				continue
			}

			// Return other errors
			return n, nil, err
		}

		// Got data, cancel the deadline
		cancel()
		break
	}

	// Retrieve hardware address and other information from addr
	sa, ok := addr.(*syscall.SockaddrLinklayer)
	if !ok || sa.Halen < 6 {
		return n, nil, syscall.EINVAL
	}

	// Use length specified to convert byte array into a hardware address slice
	mac := make(net.HardwareAddr, sa.Halen)
	copy(mac, sa.Addr[:])

	// packet(7):
	//   sll_hatype and sll_pkttype are set on received packets for your
	//   information.
	// TODO(mdlayher): determine if similar fields exist and are useful on
	// non-Linux platforms
	return n, &Addr{
		HardwareAddr: mac,
	}, nil
}

// WriteTo implements the net.PacketConn.WriteTo method.
//
// TODO(mdlayher): implement write deadlines
func (p *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) {
	// Ensure correct Addr type
	a, ok := addr.(*Addr)
	if !ok || len(a.HardwareAddr) < 6 {
		return 0, syscall.EINVAL
	}

	// Convert hardware address back to byte array form
	var baddr [8]byte
	copy(baddr[:], a.HardwareAddr)

	// Send message on socket to the specified hardware address from addr
	// packet(7):
	//   When you send packets it is enough to specify sll_family, sll_addr,
	//   sll_halen, sll_ifindex.  The other fields should  be 0.
	// In this case, sll_family is taken care of automatically by syscall
	err := p.s.Sendto(b, 0, &syscall.SockaddrLinklayer{
		Ifindex: p.ifi.Index,
		Halen:   uint8(len(a.HardwareAddr)),
		Addr:    baddr,
	})
	return len(b), err
}

// Close closes the connection.
func (p *packetConn) Close() error {
	return p.s.Close()
}

// LocalAddr returns the local network address.
func (p *packetConn) LocalAddr() net.Addr {
	return &Addr{
		HardwareAddr: p.ifi.HardwareAddr,
	}
}

// TODO(mdlayher): it is unfortunate that we have to implement deadlines using
// a context, but it appears that there may not be a better solution until
// Go 1.6 or later.  See here: https://github.com/golang/go/issues/10565.

// SetDeadline implements the net.PacketConn.SetDeadline method.
func (p *packetConn) SetDeadline(t time.Time) error {
	p.timeoutMu.Lock()
	p.rtimeout = t
	p.wtimeout = t
	p.timeoutMu.Unlock()

	return nil
}

// SetReadDeadline implements the net.PacketConn.SetReadDeadline method.
func (p *packetConn) SetReadDeadline(t time.Time) error {
	p.timeoutMu.Lock()
	p.rtimeout = t
	p.timeoutMu.Unlock()

	return nil
}

// SetWriteDeadline implements the net.PacketConn.SetWriteDeadline method.
func (p *packetConn) SetWriteDeadline(t time.Time) error {
	return ErrNotImplemented
}

// sysSocket is the default socket implementation.  It makes use of
// Linux-specific system calls to handle raw socket functionality.
type sysSocket struct {
	fd int
}

// Method implementations simply invoke the syscall of the same name, but pass
// the file descriptor stored in the sysSocket as the socket to use.
func (s *sysSocket) Bind(sa syscall.Sockaddr) error { return syscall.Bind(s.fd, sa) }
func (s *sysSocket) Close() error                   { return syscall.Close(s.fd) }
func (s *sysSocket) Recvfrom(p []byte, flags int) (int, syscall.Sockaddr, error) {
	return syscall.Recvfrom(s.fd, p, flags)
}
func (s *sysSocket) Sendto(p []byte, flags int, to syscall.Sockaddr) error {
	return syscall.Sendto(s.fd, p, flags, to)
}
func (s *sysSocket) SetNonblock(nonblocking bool) error { return syscall.SetNonblock(s.fd, nonblocking) }

// timeSleeper sleeps using time.Sleep.
type timeSleeper struct{}

func (_ timeSleeper) Sleep(d time.Duration) {
	time.Sleep(d)
}
<|endoftext|>


<commit_before>package advisors

import (
	"log"

	"github.com/ahmetb/go-linq"
	"github.com/arukim/expansion/game"
	m "github.com/arukim/expansion/models"
)

// GoldHunter is advisor with main focus to get gold mines ASAP
type GoldHunter struct {
	target   *m.Point
	forces   pointEx
	finished bool
}

func NewGoldHunter() *GoldHunter {
	return &GoldHunter{}
}

func (gh *GoldHunter) MakeTurn(b *game.Board, t *m.Turn) {
	if gh.finished {
		return
	}

findTarget:
	// it there is no target, find new one
	if gh.target == nil {
		if len(b.FreeMines) == 0 {
			log.Println("GH: no new targets, I'm done")
			gh.finished = true
			return
		}

		p := linq.From(b.FreeMines).SelectT(func(x linq.KeyValue) m.Point {
			return x.Key.(m.Point)
		}).OrderByT(func(x m.Point) int {
			return b.OutsideMap.Get(x) - 1
		}).First().(m.Point)
		log.Printf("GH: found new target at %+v\n", p)
		gh.target = &p
	}

	if b.PlayersMap.Get(*gh.target) != -1 {
		gh.target = nil
		goto findTarget
	}

	maxForce := 0
	maxForcePos := 0
	for i, v := range b.ForcesMap.Data {
		// check only my force
		if b.PlayersMap.Data[i] == b.TurnInfo.MyColor && v > maxForce {
			maxForce = v
			maxForcePos = i
		}
	}

	from := m.NewPoint(maxForcePos, b.Width)
	move := *b.GetDirectionFromTo(from, *gh.target)

	forces := b.ForcesMap.Get(from)
	dist := b.OutsideMap.Get(*gh.target)

	if dist > forces {
		move.Count = forces
	} else {
		move.Count = dist
	}

	b.ForcesMap.Data[maxForce] -= move.Count
	log.Printf("GH: moving %d forces to target %+v\n", move.Count, *gh.target)
	t.Movements = append(t.Movements, move)
}
<commit_msg>fixed bug in gold hunter<commit_after>package advisors

import (
	"log"

	"github.com/ahmetb/go-linq"
	"github.com/arukim/expansion/game"
	m "github.com/arukim/expansion/models"
)

// GoldHunter is advisor with main focus to get gold mines ASAP
type GoldHunter struct {
	target   *m.Point
	forces   pointEx
	finished bool
}

func NewGoldHunter() *GoldHunter {
	return &GoldHunter{}
}

func (gh *GoldHunter) MakeTurn(b *game.Board, t *m.Turn) {
	if gh.finished {
		return
	}

findTarget:
	// it there is no target, find new one
	if gh.target == nil {
		if len(b.FreeMines) == 0 {
			log.Println("GH: no new targets, I'm done")
			gh.finished = true
			return
		}

		p := linq.From(b.FreeMines).SelectT(func(x linq.KeyValue) m.Point {
			return x.Key.(m.Point)
		}).OrderByT(func(x m.Point) int {
			return b.OutsideMap.Get(x) - 1
		}).First().(m.Point)
		log.Printf("GH: found new target at %+v\n", p)
		gh.target = &p
	}

	if b.PlayersMap.Get(*gh.target) != -1 {
		gh.target = nil
		goto findTarget
	}

	maxForce := 0
	maxForcePos := 0
	for i, v := range b.ForcesMap.Data {
		// check only my force
		if b.PlayersMap.Data[i] == b.TurnInfo.MyColor && v > maxForce {
			maxForce = v
			maxForcePos = i
		}
	}

	from := m.NewPoint(maxForcePos, b.Width)
	move := *b.GetDirectionFromTo(from, *gh.target)

	forces := b.ForcesMap.Get(from)
	dist := b.OutsideMap.Get(*gh.target) - 1

	if dist > forces {
		move.Count = forces
	} else {
		move.Count = dist
	}

	b.ForcesMap.Data[maxForce] -= move.Count
	log.Printf("GH: moving %d forces to target %+v\n", move.Count, *gh.target)
	t.Movements = append(t.Movements, move)
}
<|endoftext|>


<commit_before>// Copyright 2012 Brian "bojo" Jones. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package redistore

import (
	"encoding/base32"
	"github.com/garyburd/redigo/redis"
	"github.com/gorilla/securecookie"
	"github.com/gorilla/sessions"
	"net/http"
	"strings"
	"time"
)

// Amount of time for cookies/redis keys to expire.
var sessionExpire int = 86400 * 30

// RediStore stores sessions in a redis backend.
type RediStore struct {
	Pool    *redis.Pool
	Codecs  []securecookie.Codec
	Options *sessions.Options // default configuration
}

// NewRediStore returns a new RediStore.
func NewRediStore(size int, network, address, password string, keyPairs ...[]byte) *RediStore {
	return &RediStore{
		// http://godoc.org/github.com/garyburd/redigo/redis#Pool
		Pool: &redis.Pool{
			MaxIdle:     size,
			IdleTimeout: 240 * time.Second,
			Dial: func() (redis.Conn, error) {
				c, err := redis.Dial(network, address)
				if err != nil {
					return nil, err
				}
				if password != "" {
					if _, err := c.Do("AUTH", password); err != nil {
						c.Close()
						return nil, err
					}
				}
				return c, err
			},
			TestOnBorrow: func(c redis.Conn, t time.Time) error {
				_, err := c.Do("PING")
				return err
			},
		},
		Codecs: securecookie.CodecsFromPairs(keyPairs...),
		Options: &sessions.Options{
			Path:   "/",
			MaxAge: sessionExpire,
		},
	}
}

// Close cleans up the redis connections.
func (s *RediStore) Close() {
	s.Pool.Close()
}

// MaxLength restricts the maximum length of new sessions to l.
// If l is 0 there is no limit to the size of a session, use with caution.
// The default for a new RediStore is 4096. Redis allows for max.
// value sizes of up to 512MB (http://redis.io/topics/data-types)
func (s *RediStore) MaxLength(l int) {
	for _, c := range s.Codecs {
		if codec, ok := c.(*securecookie.SecureCookie); ok {
			codec.MaxLength(l)
		}
	}
}

// Get returns a session for the given name after adding it to the registry.
//
// See gorilla/sessions FilesystemStore.Get().
func (s *RediStore) Get(r *http.Request, name string) (*sessions.Session, error) {
	return sessions.GetRegistry(r).Get(s, name)
}

// New returns a session for the given name without adding it to the registry.
//
// See gorilla/sessions FilesystemStore.New().
func (s *RediStore) New(r *http.Request, name string) (*sessions.Session, error) {
	var err error
	session := sessions.NewSession(s, name)
	session.Options = &(*s.Options)
	session.IsNew = true
	if c, errCookie := r.Cookie(name); errCookie == nil {
		err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.Codecs...)
		if err == nil {
			err = s.load(session)
			if err == nil {
				session.IsNew = false
			}
		}
	}
	return session, err
}

// Save adds a single session to the response.
func (s *RediStore) Save(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
	// Marked for deletion.
	if session.Options.MaxAge < 0 {
		if err := s.delete(session); err != nil {
			return err
		}
		http.SetCookie(w, sessions.NewCookie(session.Name(), "", session.Options))
	} else {
		// Build an alphanumeric key for the redis store.
		if session.ID == "" {
			session.ID = strings.TrimRight(base32.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(32)), "=")
		}
		if err := s.save(session); err != nil {
			return err
		}
		encoded, err := securecookie.EncodeMulti(session.Name(), session.ID, s.Codecs...)
		if err != nil {
			return err
		}
		http.SetCookie(w, sessions.NewCookie(session.Name(), encoded, session.Options))
	}
	return nil
}

// Delete removes the session from redis, and sets the cookie to expire.
//
// WARNING: This method should be considered deprecated since it is not exposed via the gorilla/sessions interface.
// Set session.Options.MaxAge = -1 and call Save instead. - July 18th, 2013
func (s *RediStore) Delete(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
	conn := s.Pool.Get()
	defer conn.Close()
	if _, err := conn.Do("DEL", "session_"+session.ID); err != nil {
		return err
	}
	// Set cookie to expire.
	options := *session.Options
	options.MaxAge = -1
	http.SetCookie(w, sessions.NewCookie(session.Name(), "", &options))
	// Clear session values.
	for k := range session.Values {
		delete(session.Values, k)
	}
	return nil
}

// save stores the session in redis.
func (s *RediStore) save(session *sessions.Session) error {
	encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, s.Codecs...)
	if err != nil {
		return err
	}
	conn := s.Pool.Get()
	defer conn.Close()
	if err = conn.Err(); err != nil {
		return err
	}
	_, err = conn.Do("SETEX", "session_"+session.ID, session.Options.MaxAge, encoded)
	return err
}

// load reads the session from redis.
func (s *RediStore) load(session *sessions.Session) error {
	conn := s.Pool.Get()
	defer conn.Close()
	if err := conn.Err(); err != nil {
		return err
	}
	data, err := conn.Do("GET", "session_"+session.ID)
	if err != nil {
		return err
	}
	if data == nil {
		return nil // no data was associated with this key
	}
	str, err := redis.String(data, err)
	if err != nil {
		return err
	}
	if err = securecookie.DecodeMulti(session.Name(), str, &session.Values, s.Codecs...); err != nil {
		return err
	}
	return nil
}

// delete removes keys from redis if MaxAge<0
func (s *RediStore) delete(session *sessions.Session) error {
	conn := s.Pool.Get()
	defer conn.Close()
	if _, err := conn.Do("DEL", "session_"+session.ID); err != nil {
		return err
	}
	return nil
}
<commit_msg>MaxAge = 0 does not agree wtih SETEX<commit_after>// Copyright 2012 Brian "bojo" Jones. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package redistore

import (
	"encoding/base32"
	"github.com/garyburd/redigo/redis"
	"github.com/gorilla/securecookie"
	"github.com/gorilla/sessions"
	"net/http"
	"strings"
	"time"
)

// Amount of time for cookies/redis keys to expire.
var sessionExpire int = 86400 * 30

// RediStore stores sessions in a redis backend.
type RediStore struct {
	Pool          *redis.Pool
	Codecs        []securecookie.Codec
	Options       *sessions.Options // default configuration
	DefaultMaxAge int               // default TTL for a MaxAge == 0 session
}

// NewRediStore returns a new RediStore.
func NewRediStore(size int, network, address, password string, keyPairs ...[]byte) *RediStore {
	return &RediStore{
		// http://godoc.org/github.com/garyburd/redigo/redis#Pool
		Pool: &redis.Pool{
			MaxIdle:     size,
			IdleTimeout: 240 * time.Second,
			Dial: func() (redis.Conn, error) {
				c, err := redis.Dial(network, address)
				if err != nil {
					return nil, err
				}
				if password != "" {
					if _, err := c.Do("AUTH", password); err != nil {
						c.Close()
						return nil, err
					}
				}
				return c, err
			},
			TestOnBorrow: func(c redis.Conn, t time.Time) error {
				_, err := c.Do("PING")
				return err
			},
		},
		Codecs: securecookie.CodecsFromPairs(keyPairs...),
		Options: &sessions.Options{
			Path:   "/",
			MaxAge: sessionExpire,
		},
		DefaultMaxAge: 60 * 20, // 20 minutes seems like a reasonable default
	}
}

// Close cleans up the redis connections.
func (s *RediStore) Close() {
	s.Pool.Close()
}

// MaxLength restricts the maximum length of new sessions to l.
// If l is 0 there is no limit to the size of a session, use with caution.
// The default for a new RediStore is 4096. Redis allows for max.
// value sizes of up to 512MB (http://redis.io/topics/data-types)
func (s *RediStore) MaxLength(l int) {
	for _, c := range s.Codecs {
		if codec, ok := c.(*securecookie.SecureCookie); ok {
			codec.MaxLength(l)
		}
	}
}

// Get returns a session for the given name after adding it to the registry.
//
// See gorilla/sessions FilesystemStore.Get().
func (s *RediStore) Get(r *http.Request, name string) (*sessions.Session, error) {
	return sessions.GetRegistry(r).Get(s, name)
}

// New returns a session for the given name without adding it to the registry.
//
// See gorilla/sessions FilesystemStore.New().
func (s *RediStore) New(r *http.Request, name string) (*sessions.Session, error) {
	var err error
	session := sessions.NewSession(s, name)
	session.Options = &(*s.Options)
	session.IsNew = true
	if c, errCookie := r.Cookie(name); errCookie == nil {
		err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.Codecs...)
		if err == nil {
			err = s.load(session)
			if err == nil {
				session.IsNew = false
			}
		}
	}
	return session, err
}

// Save adds a single session to the response.
func (s *RediStore) Save(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
	// Marked for deletion.
	if session.Options.MaxAge < 0 {
		if err := s.delete(session); err != nil {
			return err
		}
		http.SetCookie(w, sessions.NewCookie(session.Name(), "", session.Options))
	} else {
		// Build an alphanumeric key for the redis store.
		if session.ID == "" {
			session.ID = strings.TrimRight(base32.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(32)), "=")
		}
		if err := s.save(session); err != nil {
			return err
		}
		encoded, err := securecookie.EncodeMulti(session.Name(), session.ID, s.Codecs...)
		if err != nil {
			return err
		}
		http.SetCookie(w, sessions.NewCookie(session.Name(), encoded, session.Options))
	}
	return nil
}

// Delete removes the session from redis, and sets the cookie to expire.
//
// WARNING: This method should be considered deprecated since it is not exposed via the gorilla/sessions interface.
// Set session.Options.MaxAge = -1 and call Save instead. - July 18th, 2013
func (s *RediStore) Delete(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
	conn := s.Pool.Get()
	defer conn.Close()
	if _, err := conn.Do("DEL", "session_"+session.ID); err != nil {
		return err
	}
	// Set cookie to expire.
	options := *session.Options
	options.MaxAge = -1
	http.SetCookie(w, sessions.NewCookie(session.Name(), "", &options))
	// Clear session values.
	for k := range session.Values {
		delete(session.Values, k)
	}
	return nil
}

// save stores the session in redis.
func (s *RediStore) save(session *sessions.Session) error {
	encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, s.Codecs...)
	if err != nil {
		return err
	}
	conn := s.Pool.Get()
	defer conn.Close()
	if err = conn.Err(); err != nil {
		return err
	}
	age := session.Options.MaxAge
	if age == 0 {
		age = s.DefaultMaxAge
	}
	_, err = conn.Do("SETEX", "session_"+session.ID, age, encoded)
	return err
}

// load reads the session from redis.
func (s *RediStore) load(session *sessions.Session) error {
	conn := s.Pool.Get()
	defer conn.Close()
	if err := conn.Err(); err != nil {
		return err
	}
	data, err := conn.Do("GET", "session_"+session.ID)
	if err != nil {
		return err
	}
	if data == nil {
		return nil // no data was associated with this key
	}
	str, err := redis.String(data, err)
	if err != nil {
		return err
	}
	if err = securecookie.DecodeMulti(session.Name(), str, &session.Values, s.Codecs...); err != nil {
		return err
	}
	return nil
}

// delete removes keys from redis if MaxAge<0
func (s *RediStore) delete(session *sessions.Session) error {
	conn := s.Pool.Get()
	defer conn.Close()
	if _, err := conn.Do("DEL", "session_"+session.ID); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package generator

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"testing"

	"github.com/gulien/orbit/context"

	"gopkg.in/yaml.v2"
)

var (
	// defaultGenerator is an instance of OrbitGenerator used in this test suite which contains one values file and one
	// .env file.
	defaultGenerator *OrbitGenerator

	// manyGenerator is an instance of OrbitGenerator used in this test suite which contains two values files and two
	// .env files.
	manyGenerator *OrbitGenerator

	// expectedResult contains the data from the file "expected_result.yml"
	expectedResult interface{}
)

// init instantiates expectedResult plus the OrbitGenerator defaultGenerator and manyGenerator.
func init() {
	// retrieves the data from the file "expected_result.yml".
	expectedResultPath, err := filepath.Abs("../.assets/tests/expected_result.yml")
	if err != nil {
		panic(err)
	}

	data, err := ioutil.ReadFile(expectedResultPath)
	if err != nil {
		panic(err)
	}

	if err := yaml.Unmarshal(data, &expectedResult); err != nil {
		panic(err)
	}

	// loads assets.
	defaultTmpl, err := filepath.Abs("../.assets/tests/template.yml")
	if err != nil {
		panic(err)
	}

	values, err := filepath.Abs("../.assets/tests/values.yml")
	if err != nil {
		panic(err)
	}

	envFile, err := filepath.Abs("../.assets/tests/.env")
	if err != nil {
		panic(err)
	}

	rawData := "author=Julien Neuhart;comment=A simple file for testing purpose"

	ctx, err := context.NewOrbitContext(defaultTmpl, values, envFile, rawData)
	if err != nil {
		panic(err)
	}

	defaultGenerator = NewOrbitGenerator(ctx)

	manyTmpl, err := filepath.Abs("../.assets/tests/template_many.yml")
	if err != nil {
		panic(err)
	}

	ctx, err = context.NewOrbitContext(manyTmpl, "ru,"+values+";usa,"+values, "ru,"+envFile+";usa,"+envFile, rawData)
	if err != nil {
		panic(err)
	}

	manyGenerator = NewOrbitGenerator(ctx)
}

// Tests Parse function.
func TestOrbitGenerator_Parse(t *testing.T) {
	template, err := filepath.Abs("../.assets/tests/wrong_template.yml")
	if err != nil {
		panic(err)
	}

	ctx, err := context.NewOrbitContext(template, "", "", "")
	if err != nil {
		panic(err)
	}

	g := NewOrbitGenerator(ctx)

	if _, err := g.Parse(); err == nil {
		t.Error("OrbitGenerator should not have been able to parse the template \"wrong_template.yml\"!")
	}
}

// Tests WriteOutputFile function.
func TestOrbitGenerator_WriteOutputFile(t *testing.T) {
	dataDefaultTmpl, err := defaultGenerator.Parse()
	if err != nil {
		t.Error("Failed to parse the default template!")
	}

	if err := defaultGenerator.WriteOutputFile("result.yml", dataDefaultTmpl); err != nil {
		t.Error("Failed to write the outpout file from the default template!")
	}

	dataDefaultResult, err := ioutil.ReadFile("result.yml")
	if err != nil {
		panic(err)
	}

	var defaultResult interface{}
	if err := yaml.Unmarshal(dataDefaultResult, &defaultResult); err != nil {
		panic(err)
	}

	os.Remove("result.yml")

	if !reflect.DeepEqual(defaultResult, expectedResult) {
		t.Error("Result file from the default template should be equal to the expected result!")
	}

	dataManyTmpl, err := manyGenerator.Parse()
	if err != nil {
		t.Error("Failed to parse the many template!")
	}

	if err := manyGenerator.WriteOutputFile("result.yml", dataManyTmpl); err != nil {
		t.Error("Failed to write the outpout file from the many template!")
	}

	dataManyResult, err := ioutil.ReadFile("result.yml")
	if err != nil {
		panic(err)
	}

	var manyResult interface{}
	if err := yaml.Unmarshal(dataManyResult, &manyResult); err != nil {
		panic(err)
	}

	os.Remove("result.yml")

	if !reflect.DeepEqual(manyResult, expectedResult) {
		t.Error("Result file from the many template should be equal to the expected result!")
	}

	if err := manyGenerator.WriteOutputFile("/...", dataManyTmpl); err == nil {
		t.Error("WriteOutputFile should not been able to to write the outpout file \"/...\"!")
	}
}
<commit_msg>fixing typo in TestOrbitGenerator_WriteOutputFile<commit_after>package generator

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"testing"

	"github.com/gulien/orbit/context"

	"gopkg.in/yaml.v2"
)

var (
	// defaultGenerator is an instance of OrbitGenerator used in this test suite which contains one values file and one
	// .env file.
	defaultGenerator *OrbitGenerator

	// manyGenerator is an instance of OrbitGenerator used in this test suite which contains two values files and two
	// .env files.
	manyGenerator *OrbitGenerator

	// expectedResult contains the data from the file "expected_result.yml"
	expectedResult interface{}
)

// init instantiates expectedResult plus the OrbitGenerator defaultGenerator and manyGenerator.
func init() {
	// retrieves the data from the file "expected_result.yml".
	expectedResultPath, err := filepath.Abs("../.assets/tests/expected_result.yml")
	if err != nil {
		panic(err)
	}

	data, err := ioutil.ReadFile(expectedResultPath)
	if err != nil {
		panic(err)
	}

	if err := yaml.Unmarshal(data, &expectedResult); err != nil {
		panic(err)
	}

	// loads assets.
	defaultTmpl, err := filepath.Abs("../.assets/tests/template.yml")
	if err != nil {
		panic(err)
	}

	values, err := filepath.Abs("../.assets/tests/values.yml")
	if err != nil {
		panic(err)
	}

	envFile, err := filepath.Abs("../.assets/tests/.env")
	if err != nil {
		panic(err)
	}

	rawData := "author=Julien Neuhart;comment=A simple file for testing purpose"

	ctx, err := context.NewOrbitContext(defaultTmpl, values, envFile, rawData)
	if err != nil {
		panic(err)
	}

	defaultGenerator = NewOrbitGenerator(ctx)

	manyTmpl, err := filepath.Abs("../.assets/tests/template_many.yml")
	if err != nil {
		panic(err)
	}

	ctx, err = context.NewOrbitContext(manyTmpl, "ru,"+values+";usa,"+values, "ru,"+envFile+";usa,"+envFile, rawData)
	if err != nil {
		panic(err)
	}

	manyGenerator = NewOrbitGenerator(ctx)
}

// Tests Parse function.
func TestOrbitGenerator_Parse(t *testing.T) {
	template, err := filepath.Abs("../.assets/tests/wrong_template.yml")
	if err != nil {
		panic(err)
	}

	ctx, err := context.NewOrbitContext(template, "", "", "")
	if err != nil {
		panic(err)
	}

	g := NewOrbitGenerator(ctx)

	if _, err := g.Parse(); err == nil {
		t.Error("OrbitGenerator should not have been able to parse the template \"wrong_template.yml\"!")
	}
}

// Tests WriteOutputFile function.
func TestOrbitGenerator_WriteOutputFile(t *testing.T) {
	dataDefaultTmpl, err := defaultGenerator.Parse()
	if err != nil {
		t.Error("Failed to parse the default template!")
	}

	if err := defaultGenerator.WriteOutputFile("result.yml", dataDefaultTmpl); err != nil {
		t.Error("Failed to write the outpout file from the default template!")
	}

	dataDefaultResult, err := ioutil.ReadFile("result.yml")
	if err != nil {
		panic(err)
	}

	var defaultResult interface{}
	if err := yaml.Unmarshal(dataDefaultResult, &defaultResult); err != nil {
		panic(err)
	}

	os.Remove("result.yml")

	if !reflect.DeepEqual(defaultResult, expectedResult) {
		t.Error("Result file from the default template should be equal to the expected result!")
	}

	dataManyTmpl, err := manyGenerator.Parse()
	if err != nil {
		t.Error("Failed to parse the many template!")
	}

	if err := manyGenerator.WriteOutputFile("result.yml", dataManyTmpl); err != nil {
		t.Error("Failed to write the outpout file from the many template!")
	}

	dataManyResult, err := ioutil.ReadFile("result.yml")
	if err != nil {
		panic(err)
	}

	var manyResult interface{}
	if err := yaml.Unmarshal(dataManyResult, &manyResult); err != nil {
		panic(err)
	}

	os.Remove("result.yml")

	if !reflect.DeepEqual(manyResult, expectedResult) {
		t.Error("Result file from the many template should be equal to the expected result!")
	}

	if err := manyGenerator.WriteOutputFile("/...", dataManyTmpl); err == nil {
		t.Error("WriteOutputFile should not have been able to write the outpout file \"/...\"!")
	}
}
<|endoftext|>


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

package kubernetes

import (
	"encoding/json"
	"fmt"
	"github.com/romana/core/common"
	"github.com/romana/core/tenant"
	"log"
	"net/http"
)

const (
	selector = "podSelector"
)

// Done is an alias for empty struct, used to make broadcast channels
// for terminating goroutines.
type Done struct{}

/*
{"type":"ADDED","object":{"kind":"Namespace","apiVersion":"v1","metadata":{"name":"default","selfLink":"/api/v1/namespaces/default","uid":"d10db271-dc03-11e5-9c86-0213e1312dc5","resourceVersion":"6","creationTimestamp":"2016-02-25T21:07:45Z"},"spec":{"finalizers":["kubernetes"]},"status":{"phase":"Active"}}}
*/

// Event is a representation of a structure that we receive from kubernetes API.
type Event struct {
	Type   string     `json:"Type"`
	Object KubeObject `json:"object"`
}

const (
	KubeEventAdded         = "ADDED"
	KubeEventDeleted       = "DELETED"
	KubeEventModified      = "MODIFIED"
	InternalEventDeleteAll = "_DELETE_ALL"
)

// KubeObject is a representation of object in kubernetes.
type KubeObject struct {
	Kind       string            `json:"kind"`
	Spec       Spec              `json:"spec"`
	ApiVersion string            `json:"apiVersion"`
	Metadata   Metadata          `json:"metadata"`
	Status     map[string]string `json:"status,omitempty"`
}

// makeId makes id to identify kube object.
func (o KubeObject) makeId() string {
	id := o.Metadata.Name + "/" + o.Metadata.Namespace
	return id
}

type PodSelector map[string]string

type FromEntry struct {
	Pods PodSelector `json:"pods"`
}

type AllowIncoming struct {
	From    []FromEntry `json:"from"`
	ToPorts []ToPort    `json:"toPorts"`
}

type ToPort struct {
	Port     uint   `json:"port"`
	Protocol string `json:"protocol"`
}

// TODO need to find a way to use different specs for different resources.
type Spec struct {
	AllowIncoming AllowIncoming `json:"allowIncoming"`
	PodSelector   PodSelector   `json:"podSelector"`
}

// Metadata is a representation of metadata in kubernetes object
type Metadata struct {
	Name              string            `json:"name"`
	Namespace         string            `json:"namespace"`
	SelfLink          string            `json:"selfLink"`
	Uid               string            `json:"uid"`
	ResourceVersion   string            `json:"resourceVersion"`
	CreationTimestamp string            `json:"creationTimestamp"`
	Labels            map[string]string `json:"labels"`
	Annotations       map[string]string `json:"annotations,omitempty"`
}

// handle kubernetes events according to their type.
func (e Event) handle(l *kubeListener) {
	log.Printf("Processing %s request for %s", e.Type, e.Object.Metadata.Name)

	if e.Object.Kind == "NetworkPolicy" && e.Type != KubeEventModified {
		e.handleNetworkPolicyEvent(l)
	} else if e.Object.Kind == "Namespace" {
		e.handleNamespaceEvent(l)
	} else {
		log.Printf("Received unindentified request %s for %s", e.Type, e.Object.Metadata.Name)
	}
}

// handleNetworkPolicyEvent by creating or deleting romana policies.
func (e Event) handleNetworkPolicyEvent(l *kubeListener) {
	var action networkPolicyAction
	if e.Type == KubeEventAdded {
		action = networkPolicyActionAdd
	} else {
		action = networkPolicyActionDelete
	}
	policy, err := l.translateNetworkPolicy(&e.Object)
	if err == nil {
		l.applyNetworkPolicy(action, policy)
	} else {
		log.Println(err)
	}
}

// handleNamespaceEvent by creating or deleting romana tenants.
func (e Event) handleNamespaceEvent(l *kubeListener) {
	log.Printf("Processing namespace event == %v and phase %v", e.Type, e.Object.Status)

	if e.Type == KubeEventAdded {
		tenantReq := tenant.Tenant{Name: e.Object.Metadata.Name, ExternalID: e.Object.Metadata.Name}
		tenantResp := tenant.Tenant{}
		log.Printf("processor: Posting to /tenants: %#v", tenantReq)
		tenantUrl, err := l.restClient.GetServiceUrl("tenant")
		if err != nil {
			log.Printf("Error adding tenant %s: %#v", tenantReq.Name, err)
		} else {
			err := l.restClient.Post(fmt.Sprintf("%s/tenants", tenantUrl), tenantReq, &tenantResp)
			if err != nil {
				log.Printf("Error adding tenant %s: %#v", tenantReq.Name, err)
			} else {
				log.Printf("Added tenant: %#v", tenantResp)
			}
		}
	} else {
		// TODO finish once UUID is merged
		// tenantReq := tenant.Tenant{Name: e.Object.Metadata.Name}
		// tenantResp := tenant.Tenant{}
		// err = client.Delete("/tenants", tenantReq, &tenantResp)
		// if err != nil {
		// 	log.Printf("Error adding tenant %s: %#v", tenantReq.Name, err)
		// } else {
		// 	log.Printf("Added tenant: %#v", tenantResp)
		// }
	}

	// Ignore repeated events during namespace termination
	if e.Object.Status["phase"] == "Terminating" {
		if e.Type != KubeEventModified {
			e.Object.handleAnnotations(l)
		}
	} else {
		e.Object.handleAnnotations(l)
	}

}

// handleAnnotations on a namespace by implementing extra features requested through the annotation
func (o KubeObject) handleAnnotations(l *kubeListener) {
	log.Printf("In handleAnnotations")

	if o.Kind != "Namespace" {
		log.Printf("Error handling annotations on a namespace - object is not a namespace %s \n", o.Kind)
		return
	}

	CreateDefaultPolicy(o, l)
}

func CreateDefaultPolicy(o KubeObject, l *kubeListener) {
	log.Printf("In CreateDefaultPolicy for %v\n", o)
	tenant, _, err := l.resolveTenantByName(o.Metadata.Name)
	if err != nil {
		log.Printf("In CreateDefaultPolicy :: Error :: failed to resolve tenant %s \n", err)
	}

	policyName := fmt.Sprintf("ns%d", tenant.ID)

	romanaPolicy := &common.Policy{
		Direction: common.PolicyDirectionIngress,
		Name:      policyName,
		AppliedTo: []common.Endpoint{{TenantNetworkID: tenant.ID}},
		Peers:     []common.Endpoint{{Peer: "any"}},
		Rules:     []common.Rule{{Protocol: "any"}},
	}

	log.Printf("In CreateDefaultPolicy with policy %v\n", romanaPolicy)

	var desiredAction networkPolicyAction

	if isolation, ok := o.Metadata.Annotations["net.alpha.kubernetes.io/network-isolation"]; ok {
		log.Printf("Handling default policy on a namespace %s, isolation is now %s \n", o.Metadata.Name, isolation)
		switch isolation {
		case "on":
			desiredAction = networkPolicyActionAdd
		case "off":
			desiredAction = networkPolicyActionDelete
		default:
			log.Printf("In CreateDefaultPolicy :: Error :: unrecognised annotation on a namespace %s is %s (expected on|off) \n",
				o.Metadata.Name, isolation)
			return
		}

	} else {
		log.Printf("Handling default policy on a namespace, no annotation detected assuming non isolated namespace\n")
		desiredAction = networkPolicyActionAdd
	}

	if err2 := l.applyNetworkPolicy(desiredAction, *romanaPolicy); err2 != nil {
		log.Printf("In CreateDefaultPolicy :: Error :: failed to apply %v to the policy %s \n", desiredAction, err)
	}
}

// watchEvents maintains goroutine fired by NsWatch, restarts it in case HTTP GET times out.
func (l *kubeListener) watchEvents(done <-chan Done, url string, resp *http.Response, out chan Event) {
	log.Println("Received namespace related event from kubernetes", resp.Body)

	dec := json.NewDecoder(resp.Body)
	var e Event

	for {
		select {
		case <-done:
			return
		default:
			// Flush e to ensure nothing gets carried over
			e = Event{}

			// Attempting to read event from HTTP connection
			err := dec.Decode(&e)
			if err != nil {
				// If fail
				log.Printf("Failed to decode message from connection %s due to %s\n. Attempting to re-establish", url, err)
				// Then stop all goroutines
				out <- Event{Type: InternalEventDeleteAll}

				// And try to re-establish HTTP connection
				resp, err2 := http.Get(url)
				if err2 != nil {
					log.Printf("Failed establish connection %s due to %s\n.", url, err)
				} else if err2 == nil {
					dec = json.NewDecoder(resp.Body)
				}
			} else {
				// Else submit event
				out <- e
			}
		}

	}
}

// NsWatch is a generator that watches namespace related events in
// kubernetes API and publishes this events to a channel.
func (l *kubeListener) nsWatch(done <-chan Done, url string) (<-chan Event, error) {
	out := make(chan Event)

	resp, err := http.Get(url)
	if err != nil {
		return out, err
	}

	go l.watchEvents(done, url, resp, out)

	return out, nil
}

// Produce method listens for resource updates happening within given namespace
// and publishes these updates in a channel.
func (ns KubeObject) produce(out chan Event, done <-chan Done, kubeListener *kubeListener) error {
	url, err := common.CleanURL(fmt.Sprintf("%s/%s/%s%s", kubeListener.kubeURL, kubeListener.policyNotificationPathPrefix, ns.Metadata.Name, kubeListener.policyNotificationPathPostfix))
	if err != nil {
		return err
	}
	log.Printf("Launching producer to listen for policy notifications on namespace %s at URL %s ", ns.Metadata.Name, url)
	resp, err := http.Get(url)
	if err != nil {
		return err
	}

	go kubeListener.watchEvents(done, url, resp, out)

	return nil
}
<commit_msg>Convert NetworkIDs in common.Endpoint into the pointer type to support no value<commit_after>// Copyright (c) 2016 Pani Networks
// All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package kubernetes

import (
	"encoding/json"
	"fmt"
	"github.com/romana/core/common"
	"github.com/romana/core/tenant"
	"log"
	"net/http"
)

const (
	selector = "podSelector"
)

// Done is an alias for empty struct, used to make broadcast channels
// for terminating goroutines.
type Done struct{}

/*
{"type":"ADDED","object":{"kind":"Namespace","apiVersion":"v1","metadata":{"name":"default","selfLink":"/api/v1/namespaces/default","uid":"d10db271-dc03-11e5-9c86-0213e1312dc5","resourceVersion":"6","creationTimestamp":"2016-02-25T21:07:45Z"},"spec":{"finalizers":["kubernetes"]},"status":{"phase":"Active"}}}
*/

// Event is a representation of a structure that we receive from kubernetes API.
type Event struct {
	Type   string     `json:"Type"`
	Object KubeObject `json:"object"`
}

const (
	KubeEventAdded         = "ADDED"
	KubeEventDeleted       = "DELETED"
	KubeEventModified      = "MODIFIED"
	InternalEventDeleteAll = "_DELETE_ALL"
)

// KubeObject is a representation of object in kubernetes.
type KubeObject struct {
	Kind       string            `json:"kind"`
	Spec       Spec              `json:"spec"`
	ApiVersion string            `json:"apiVersion"`
	Metadata   Metadata          `json:"metadata"`
	Status     map[string]string `json:"status,omitempty"`
}

// makeId makes id to identify kube object.
func (o KubeObject) makeId() string {
	id := o.Metadata.Name + "/" + o.Metadata.Namespace
	return id
}

type PodSelector map[string]string

type FromEntry struct {
	Pods PodSelector `json:"pods"`
}

type AllowIncoming struct {
	From    []FromEntry `json:"from"`
	ToPorts []ToPort    `json:"toPorts"`
}

type ToPort struct {
	Port     uint   `json:"port"`
	Protocol string `json:"protocol"`
}

// TODO need to find a way to use different specs for different resources.
type Spec struct {
	AllowIncoming AllowIncoming `json:"allowIncoming"`
	PodSelector   PodSelector   `json:"podSelector"`
}

// Metadata is a representation of metadata in kubernetes object
type Metadata struct {
	Name              string            `json:"name"`
	Namespace         string            `json:"namespace"`
	SelfLink          string            `json:"selfLink"`
	Uid               string            `json:"uid"`
	ResourceVersion   string            `json:"resourceVersion"`
	CreationTimestamp string            `json:"creationTimestamp"`
	Labels            map[string]string `json:"labels"`
	Annotations       map[string]string `json:"annotations,omitempty"`
}

// handle kubernetes events according to their type.
func (e Event) handle(l *kubeListener) {
	log.Printf("Processing %s request for %s", e.Type, e.Object.Metadata.Name)

	if e.Object.Kind == "NetworkPolicy" && e.Type != KubeEventModified {
		e.handleNetworkPolicyEvent(l)
	} else if e.Object.Kind == "Namespace" {
		e.handleNamespaceEvent(l)
	} else {
		log.Printf("Received unindentified request %s for %s", e.Type, e.Object.Metadata.Name)
	}
}

// handleNetworkPolicyEvent by creating or deleting romana policies.
func (e Event) handleNetworkPolicyEvent(l *kubeListener) {
	var action networkPolicyAction
	if e.Type == KubeEventAdded {
		action = networkPolicyActionAdd
	} else {
		action = networkPolicyActionDelete
	}
	policy, err := l.translateNetworkPolicy(&e.Object)
	if err == nil {
		l.applyNetworkPolicy(action, policy)
	} else {
		log.Println(err)
	}
}

// handleNamespaceEvent by creating or deleting romana tenants.
func (e Event) handleNamespaceEvent(l *kubeListener) {
	log.Printf("Processing namespace event == %v and phase %v", e.Type, e.Object.Status)

	if e.Type == KubeEventAdded {
		tenantReq := tenant.Tenant{Name: e.Object.Metadata.Name, ExternalID: e.Object.Metadata.Name}
		tenantResp := tenant.Tenant{}
		log.Printf("processor: Posting to /tenants: %#v", tenantReq)
		tenantUrl, err := l.restClient.GetServiceUrl("tenant")
		if err != nil {
			log.Printf("Error adding tenant %s: %#v", tenantReq.Name, err)
		} else {
			err := l.restClient.Post(fmt.Sprintf("%s/tenants", tenantUrl), tenantReq, &tenantResp)
			if err != nil {
				log.Printf("Error adding tenant %s: %#v", tenantReq.Name, err)
			} else {
				log.Printf("Added tenant: %#v", tenantResp)
			}
		}
	} else {
		// TODO finish once UUID is merged
		// tenantReq := tenant.Tenant{Name: e.Object.Metadata.Name}
		// tenantResp := tenant.Tenant{}
		// err = client.Delete("/tenants", tenantReq, &tenantResp)
		// if err != nil {
		// 	log.Printf("Error adding tenant %s: %#v", tenantReq.Name, err)
		// } else {
		// 	log.Printf("Added tenant: %#v", tenantResp)
		// }
	}

	// Ignore repeated events during namespace termination
	if e.Object.Status["phase"] == "Terminating" {
		if e.Type != KubeEventModified {
			e.Object.handleAnnotations(l)
		}
	} else {
		e.Object.handleAnnotations(l)
	}

}

// handleAnnotations on a namespace by implementing extra features requested through the annotation
func (o KubeObject) handleAnnotations(l *kubeListener) {
	log.Printf("In handleAnnotations")

	if o.Kind != "Namespace" {
		log.Printf("Error handling annotations on a namespace - object is not a namespace %s \n", o.Kind)
		return
	}

	CreateDefaultPolicy(o, l)
}

func CreateDefaultPolicy(o KubeObject, l *kubeListener) {
	log.Printf("In CreateDefaultPolicy for %v\n", o)
	tenant, _, err := l.resolveTenantByName(o.Metadata.Name)
	if err != nil {
		log.Printf("In CreateDefaultPolicy :: Error :: failed to resolve tenant %s \n", err)
	}

	policyName := fmt.Sprintf("ns%d", tenant.Seq)

	romanaPolicy := &common.Policy{
		Direction: common.PolicyDirectionIngress,
		Name:      policyName,
		AppliedTo: []common.Endpoint{{TenantNetworkID: &tenant.Seq}},
		Peers:     []common.Endpoint{{Peer: "any"}},
		Rules:     []common.Rule{{Protocol: "any"}},
	}

	log.Printf("In CreateDefaultPolicy with policy %v\n", romanaPolicy)

	var desiredAction networkPolicyAction

	if isolation, ok := o.Metadata.Annotations["net.alpha.kubernetes.io/network-isolation"]; ok {
		log.Printf("Handling default policy on a namespace %s, isolation is now %s \n", o.Metadata.Name, isolation)
		switch isolation {
		case "on":
			desiredAction = networkPolicyActionAdd
		case "off":
			desiredAction = networkPolicyActionDelete
		default:
			log.Printf("In CreateDefaultPolicy :: Error :: unrecognised annotation on a namespace %s is %s (expected on|off) \n",
				o.Metadata.Name, isolation)
			return
		}

	} else {
		log.Printf("Handling default policy on a namespace, no annotation detected assuming non isolated namespace\n")
		desiredAction = networkPolicyActionAdd
	}

	if err2 := l.applyNetworkPolicy(desiredAction, *romanaPolicy); err2 != nil {
		log.Printf("In CreateDefaultPolicy :: Error :: failed to apply %v to the policy %s \n", desiredAction, err)
	}
}

// watchEvents maintains goroutine fired by NsWatch, restarts it in case HTTP GET times out.
func (l *kubeListener) watchEvents(done <-chan Done, url string, resp *http.Response, out chan Event) {
	log.Println("Received namespace related event from kubernetes", resp.Body)

	dec := json.NewDecoder(resp.Body)
	var e Event

	for {
		select {
		case <-done:
			return
		default:
			// Flush e to ensure nothing gets carried over
			e = Event{}

			// Attempting to read event from HTTP connection
			err := dec.Decode(&e)
			if err != nil {
				// If fail
				log.Printf("Failed to decode message from connection %s due to %s\n. Attempting to re-establish", url, err)
				// Then stop all goroutines
				out <- Event{Type: InternalEventDeleteAll}

				// And try to re-establish HTTP connection
				resp, err2 := http.Get(url)
				if err2 != nil {
					log.Printf("Failed establish connection %s due to %s\n.", url, err)
				} else if err2 == nil {
					dec = json.NewDecoder(resp.Body)
				}
			} else {
				// Else submit event
				out <- e
			}
		}

	}
}

// NsWatch is a generator that watches namespace related events in
// kubernetes API and publishes this events to a channel.
func (l *kubeListener) nsWatch(done <-chan Done, url string) (<-chan Event, error) {
	out := make(chan Event)

	resp, err := http.Get(url)
	if err != nil {
		return out, err
	}

	go l.watchEvents(done, url, resp, out)

	return out, nil
}

// Produce method listens for resource updates happening within given namespace
// and publishes these updates in a channel.
func (ns KubeObject) produce(out chan Event, done <-chan Done, kubeListener *kubeListener) error {
	url, err := common.CleanURL(fmt.Sprintf("%s/%s/%s%s", kubeListener.kubeURL, kubeListener.policyNotificationPathPrefix, ns.Metadata.Name, kubeListener.policyNotificationPathPostfix))
	if err != nil {
		return err
	}
	log.Printf("Launching producer to listen for policy notifications on namespace %s at URL %s ", ns.Metadata.Name, url)
	resp, err := http.Get(url)
	if err != nil {
		return err
	}

	go kubeListener.watchEvents(done, url, resp, out)

	return nil
}
<|endoftext|>