<commit_before>// Package edit implements a command line editor.
package edit

import (
	"fmt"
	"os"
	"strings"
	"syscall"

	"github.com/elves/elvish/eval"
	"github.com/elves/elvish/store"
	"github.com/elves/elvish/sys"
)

const (
	lackEOLRune = '\u23ce'
	lackEOL     = "\033[7m" + string(lackEOLRune) + "\033[m"
)

type bufferMode int

const (
	modeInsert bufferMode = iota
	modeCommand
	modeCompletion
	modeNavigation
	modeHistory
)

type editorState struct {
	// States used during ReadLine. Reset at the beginning of ReadLine.
	active                bool
	savedTermios          *sys.Termios
	tokens                []Token
	prompt, rprompt, line string
	dot                   int
	tips                  []string
	mode                  bufferMode
	completion            *completion
	completionLines       int
	navigation            *navigation
	history               history
	isExternal            map[string]bool
	// Used for builtins.
	lastKey    Key
	nextAction action
}

type history struct {
	current int
	prefix  string
	line    string
}

// Editor keeps the status of the line editor.
type Editor struct {
	file      *os.File
	writer    *writer
	reader    *Reader
	sigs      <-chan os.Signal
	histories []string
	store     *store.Store
	evaler    *eval.Evaler
	cmdSeq    int
	editorState
}

// LineRead is the result of ReadLine. Exactly one member is non-zero, making
// it effectively a tagged union.
type LineRead struct {
	Line string
	EOF  bool
	Err  error
}

func (h *history) jump(i int, line string) {
	h.current = i
	h.line = line
}

func (ed *Editor) appendHistory(line string) {
	ed.histories = append(ed.histories, line)
	if ed.store != nil {
		ed.store.AddCmd(line)
		// TODO(xiaq): Report possible error
	}
}

func lastHistory(histories []string, upto int, prefix string) (int, string) {
	for i := upto - 1; i >= 0; i-- {
		if strings.HasPrefix(histories[i], prefix) {
			return i, histories[i]
		}
	}
	return -1, ""
}

func firstHistory(histories []string, from int, prefix string) (int, string) {
	for i := from; i < len(histories); i++ {
		if strings.HasPrefix(histories[i], prefix) {
			return i, histories[i]
		}
	}
	return -1, ""
}

func (ed *Editor) prevHistory() bool {
	if ed.history.current > 0 {
		// Session history
		i, line := lastHistory(ed.histories, ed.history.current, ed.history.prefix)
		if i >= 0 {
			ed.history.jump(i, line)
			return true
		}
	}

	if ed.store != nil {
		// Persistent history
		upto := ed.cmdSeq + min(0, ed.history.current)
		i, line, err := ed.store.LastCmd(upto, ed.history.prefix)
		if err == nil {
			ed.history.jump(i-ed.cmdSeq, line)
			return true
		}
	}
	// TODO(xiaq): Errors other than ErrNoMatchingCmd should be reported
	return false
}

func (ed *Editor) nextHistory() bool {
	if ed.store != nil {
		// Persistent history
		if ed.history.current < -1 {
			from := ed.cmdSeq + ed.history.current + 1
			i, line, err := ed.store.FirstCmd(from, ed.history.prefix)
			if err == nil {
				ed.history.jump(i-ed.cmdSeq, line)
				return true
			}
			// TODO(xiaq): Errors other than ErrNoMatchingCmd should be reported
		}
	}

	from := max(0, ed.history.current+1)
	i, line := firstHistory(ed.histories, from, ed.history.prefix)
	if i >= 0 {
		ed.history.jump(i, line)
		return true
	}
	return false
}

// NewEditor creates an Editor.
func NewEditor(file *os.File, sigs <-chan os.Signal, ev *eval.Evaler, st *store.Store) *Editor {
	seq := -1
	if st != nil {
		var err error
		seq, err = st.NextCmdSeq()
		if err != nil {
			// TODO(xiaq): Also report the error
			seq = -1
		}
	}

	ed := &Editor{
		file:   file,
		writer: newWriter(file),
		reader: NewReader(file),
		sigs:   sigs,
		store:  st,
		evaler: ev,
		cmdSeq: seq,
	}
	ev.Editor = ed
	return ed
}

func (ed *Editor) flash() {
	// TODO implement fish-like flash effect
}

func (ed *Editor) pushTip(more string) {
	ed.tips = append(ed.tips, more)
}

func (ed *Editor) refresh() error {
	ed.reader.Stop()
	defer ed.reader.Continue()
	// Re-lex the line, unless we are in modeCompletion
	if ed.mode != modeCompletion {
		// XXX Ignore error
		ed.tokens, _ = tokenize(ed.line)
		for i, t := range ed.tokens {
			for _, colorist := range colorists {
				ed.tokens[i].MoreStyle += colorist(t.Node, ed)
			}
		}
	}
	return ed.writer.refresh(&ed.editorState)
}

// TODO Allow modifiable keybindings.
var keyBindings = map[bufferMode]map[Key]string{
	modeCommand: map[Key]string{
		Key{'i', 0}:    "start-insert",
		Key{'h', 0}:    "move-dot-left",
		Key{'l', 0}:    "move-dot-right",
		Key{'D', 0}:    "kill-line-right",
		DefaultBinding: "default-command",
	},
	modeInsert: map[Key]string{
		Key{'[', Ctrl}:    "start-command",
		Key{'U', Ctrl}:    "kill-line-left",
		Key{'K', Ctrl}:    "kill-line-right",
		Key{'W', Ctrl}:    "kill-word-left",
		Key{Backspace, 0}: "kill-rune-left",
		// Some terminal send ^H on backspace
		Key{'H', Ctrl}:  "kill-rune-left",
		Key{Delete, 0}:  "kill-rune-right",
		Key{Left, 0}:    "move-dot-left",
		Key{Right, 0}:   "move-dot-right",
		Key{Up, 0}:      "move-dot-up",
		Key{Down, 0}:    "move-dot-down",
		Key{Enter, Alt}: "insert-key",
		Key{Enter, 0}:   "return-line",
		Key{'D', Ctrl}:  "return-eof",
		Key{Tab, 0}:     "start-completion",
		Key{PageUp, 0}:  "start-history",
		Key{'N', Ctrl}:  "start-navigation",
		DefaultBinding:  "default-insert",
	},
	modeCompletion: map[Key]string{
		Key{'[', Ctrl}: "cancel-completion",
		Key{Up, 0}:     "select-cand-up",
		Key{Down, 0}:   "select-cand-down",
		Key{Left, 0}:   "select-cand-left",
		Key{Right, 0}:  "select-cand-right",
		// Key{Tab, 0}:    "cycle-cand-right",
		DefaultBinding: "default-completion",
	},
	modeNavigation: map[Key]string{
		Key{Up, 0}:     "select-nav-up",
		Key{Down, 0}:   "select-nav-down",
		Key{Left, 0}:   "ascend-nav",
		Key{Right, 0}:  "descend-nav",
		DefaultBinding: "default-navigation",
	},
	modeHistory: map[Key]string{
		Key{'[', Ctrl}:   "start-insert",
		Key{PageUp, 0}:   "select-history-prev",
		Key{PageDown, 0}: "select-history-next",
		DefaultBinding:   "default-history",
	},
}

func init() {
	for _, kb := range keyBindings {
		for _, name := range kb {
			if builtins[name] == nil {
				panic("bad keyBindings table: no editor builtin named " + name)
			}
		}
	}
}

// acceptCompletion accepts currently selected completion candidate.
func (ed *Editor) acceptCompletion() {
	c := ed.completion
	if 0 <= c.current && c.current < len(c.candidates) {
		accepted := c.candidates[c.current].source.text
		// Insert the accepted completion text at ed.dot and move ed.dot after
		// the newly inserted text
		ed.line = ed.line[:ed.dot] + accepted + ed.line[ed.dot:]
		ed.dot += len(accepted)
	}
	ed.completion = nil
	ed.mode = modeInsert
}

// acceptHistory accepts currently history.
func (ed *Editor) acceptHistory() {
	ed.line = ed.history.line
	ed.dot = len(ed.line)
}

func setupTerminal(file *os.File) (*sys.Termios, error) {
	fd := int(file.Fd())
	term, err := sys.NewTermiosFromFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't get terminal attribute: %s", err)
	}

	savedTermios := term.Copy()

	term.SetICanon(false)
	term.SetEcho(false)
	term.SetVMin(1)
	term.SetVTime(0)

	err = term.ApplyToFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't set up terminal attribute: %s", err)
	}

	err = sys.FlushInput(fd)
	if err != nil {
		return nil, fmt.Errorf("can't flush input: %s", err)
	}

	return savedTermios, nil
}

// startsReadLine prepares the terminal for the editor.
func (ed *Editor) startReadLine() error {
	savedTermios, err := setupTerminal(ed.file)
	if err != nil {
		return err
	}
	ed.savedTermios = savedTermios

	_, width := sys.GetWinsize(int(ed.file.Fd()))
	// Turn on autowrap, write lackEOL along with enough padding to fill the
	// whole screen. If the cursor was in the first column, we end up in the
	// same line (just off the line boundary); otherwise we are now in the next
	// line. We now rewind to the first column and erase anything there. The
	// final effect is that a lackEOL gets written if and only if the cursor
	// was not in the first column.
	//
	// After that, we turn off autowrap. The editor has its own wrapping
	// machanism.
	fmt.Fprintf(ed.file, "\033[?7h%s%*s\r \r\033[?7l", lackEOL, width-WcWidth(lackEOLRune), "")

	return nil
}

// finishReadLine puts the terminal in a state suitable for other programs to
// use.
func (ed *Editor) finishReadLine(lr *LineRead) {
	if lr.EOF == false && lr.Err == nil && lr.Line != "" {
		ed.appendHistory(lr.Line)
	}

	ed.mode = modeInsert
	ed.tips = nil
	ed.completion = nil
	ed.navigation = nil
	ed.dot = len(ed.line)
	// TODO Perhaps make it optional to NOT clear the rprompt
	ed.rprompt = ""
	ed.refresh() // XXX(xiaq): Ignore possible error
	ed.file.WriteString("\n")

	ed.reader.Stop()

	// turn on autowrap
	file.WriteString("\033[?7h")

	// restore termios
	err := ed.savedTermios.ApplyToFd(int(ed.file.Fd()))

	if err != nil {
		// BUG(xiaq): Error in Editor.finishReadLine may override earlier error
		*lr = LineRead{Err: fmt.Errorf("can't restore terminal attribute: %s", err)}
	}
	ed.savedTermios = nil
	ed.editorState = editorState{}
}

// ReadLine reads a line interactively.
// TODO(xiaq): ReadLine currently handles SIGINT and SIGWINCH and swallows all
// other signals.
func (ed *Editor) ReadLine(prompt, rprompt func() string) (lr LineRead) {
	ed.editorState = editorState{active: true}
	go ed.updateIsExternal()

	ed.writer.oldBuf.cells = nil
	ones := ed.reader.Chan()

	err := ed.startReadLine()
	if err != nil {
		return LineRead{Err: err}
	}
	defer ed.finishReadLine(&lr)

MainLoop:
	for {
		ed.prompt = prompt()
		ed.rprompt = rprompt()

		err := ed.refresh()
		if err != nil {
			return LineRead{Err: err}
		}

		ed.tips = nil

		select {
		case sig := <-ed.sigs:
			// TODO(xiaq): Maybe support customizable handling of signals
			switch sig {
			case syscall.SIGINT:
				// Start over
				ed.editorState = editorState{savedTermios: ed.savedTermios}
				goto MainLoop
			case syscall.SIGWINCH:
				continue MainLoop
			}
		case or := <-ones:
			// Alert about error
			err := or.Err
			if err != nil {
				ed.pushTip(err.Error())
				continue
			}

			// Ignore bogus CPR
			if or.CPR != invalidPos {
				continue
			}

			k := or.Key
		lookupKey:
			keyBinding, ok := keyBindings[ed.mode]
			if !ok {
				ed.pushTip("No binding for current mode")
				continue
			}

			name, bound := keyBinding[k]
			if !bound {
				name = keyBinding[DefaultBinding]
			}

			ed.lastKey = k
			builtins[name](ed)
			act := ed.nextAction
			ed.nextAction = action{}

			switch act.actionType {
			case noAction:
				continue
			case reprocessKey:
				err = ed.refresh()
				if err != nil {
					return LineRead{Err: err}
				}
				goto lookupKey
			case exitReadLine:
				return act.returnValue
			}
		}
	}
}
<commit_msg>edit: fix stupid error...<commit_after>// Package edit implements a command line editor.
package edit

import (
	"fmt"
	"os"
	"strings"
	"syscall"

	"github.com/elves/elvish/eval"
	"github.com/elves/elvish/store"
	"github.com/elves/elvish/sys"
)

const (
	lackEOLRune = '\u23ce'
	lackEOL     = "\033[7m" + string(lackEOLRune) + "\033[m"
)

type bufferMode int

const (
	modeInsert bufferMode = iota
	modeCommand
	modeCompletion
	modeNavigation
	modeHistory
)

type editorState struct {
	// States used during ReadLine. Reset at the beginning of ReadLine.
	active                bool
	savedTermios          *sys.Termios
	tokens                []Token
	prompt, rprompt, line string
	dot                   int
	tips                  []string
	mode                  bufferMode
	completion            *completion
	completionLines       int
	navigation            *navigation
	history               history
	isExternal            map[string]bool
	// Used for builtins.
	lastKey    Key
	nextAction action
}

type history struct {
	current int
	prefix  string
	line    string
}

// Editor keeps the status of the line editor.
type Editor struct {
	file      *os.File
	writer    *writer
	reader    *Reader
	sigs      <-chan os.Signal
	histories []string
	store     *store.Store
	evaler    *eval.Evaler
	cmdSeq    int
	editorState
}

// LineRead is the result of ReadLine. Exactly one member is non-zero, making
// it effectively a tagged union.
type LineRead struct {
	Line string
	EOF  bool
	Err  error
}

func (h *history) jump(i int, line string) {
	h.current = i
	h.line = line
}

func (ed *Editor) appendHistory(line string) {
	ed.histories = append(ed.histories, line)
	if ed.store != nil {
		ed.store.AddCmd(line)
		// TODO(xiaq): Report possible error
	}
}

func lastHistory(histories []string, upto int, prefix string) (int, string) {
	for i := upto - 1; i >= 0; i-- {
		if strings.HasPrefix(histories[i], prefix) {
			return i, histories[i]
		}
	}
	return -1, ""
}

func firstHistory(histories []string, from int, prefix string) (int, string) {
	for i := from; i < len(histories); i++ {
		if strings.HasPrefix(histories[i], prefix) {
			return i, histories[i]
		}
	}
	return -1, ""
}

func (ed *Editor) prevHistory() bool {
	if ed.history.current > 0 {
		// Session history
		i, line := lastHistory(ed.histories, ed.history.current, ed.history.prefix)
		if i >= 0 {
			ed.history.jump(i, line)
			return true
		}
	}

	if ed.store != nil {
		// Persistent history
		upto := ed.cmdSeq + min(0, ed.history.current)
		i, line, err := ed.store.LastCmd(upto, ed.history.prefix)
		if err == nil {
			ed.history.jump(i-ed.cmdSeq, line)
			return true
		}
	}
	// TODO(xiaq): Errors other than ErrNoMatchingCmd should be reported
	return false
}

func (ed *Editor) nextHistory() bool {
	if ed.store != nil {
		// Persistent history
		if ed.history.current < -1 {
			from := ed.cmdSeq + ed.history.current + 1
			i, line, err := ed.store.FirstCmd(from, ed.history.prefix)
			if err == nil {
				ed.history.jump(i-ed.cmdSeq, line)
				return true
			}
			// TODO(xiaq): Errors other than ErrNoMatchingCmd should be reported
		}
	}

	from := max(0, ed.history.current+1)
	i, line := firstHistory(ed.histories, from, ed.history.prefix)
	if i >= 0 {
		ed.history.jump(i, line)
		return true
	}
	return false
}

// NewEditor creates an Editor.
func NewEditor(file *os.File, sigs <-chan os.Signal, ev *eval.Evaler, st *store.Store) *Editor {
	seq := -1
	if st != nil {
		var err error
		seq, err = st.NextCmdSeq()
		if err != nil {
			// TODO(xiaq): Also report the error
			seq = -1
		}
	}

	ed := &Editor{
		file:   file,
		writer: newWriter(file),
		reader: NewReader(file),
		sigs:   sigs,
		store:  st,
		evaler: ev,
		cmdSeq: seq,
	}
	ev.Editor = ed
	return ed
}

func (ed *Editor) flash() {
	// TODO implement fish-like flash effect
}

func (ed *Editor) pushTip(more string) {
	ed.tips = append(ed.tips, more)
}

func (ed *Editor) refresh() error {
	ed.reader.Stop()
	defer ed.reader.Continue()
	// Re-lex the line, unless we are in modeCompletion
	if ed.mode != modeCompletion {
		// XXX Ignore error
		ed.tokens, _ = tokenize(ed.line)
		for i, t := range ed.tokens {
			for _, colorist := range colorists {
				ed.tokens[i].MoreStyle += colorist(t.Node, ed)
			}
		}
	}
	return ed.writer.refresh(&ed.editorState)
}

// TODO Allow modifiable keybindings.
var keyBindings = map[bufferMode]map[Key]string{
	modeCommand: map[Key]string{
		Key{'i', 0}:    "start-insert",
		Key{'h', 0}:    "move-dot-left",
		Key{'l', 0}:    "move-dot-right",
		Key{'D', 0}:    "kill-line-right",
		DefaultBinding: "default-command",
	},
	modeInsert: map[Key]string{
		Key{'[', Ctrl}:    "start-command",
		Key{'U', Ctrl}:    "kill-line-left",
		Key{'K', Ctrl}:    "kill-line-right",
		Key{'W', Ctrl}:    "kill-word-left",
		Key{Backspace, 0}: "kill-rune-left",
		// Some terminal send ^H on backspace
		Key{'H', Ctrl}:  "kill-rune-left",
		Key{Delete, 0}:  "kill-rune-right",
		Key{Left, 0}:    "move-dot-left",
		Key{Right, 0}:   "move-dot-right",
		Key{Up, 0}:      "move-dot-up",
		Key{Down, 0}:    "move-dot-down",
		Key{Enter, Alt}: "insert-key",
		Key{Enter, 0}:   "return-line",
		Key{'D', Ctrl}:  "return-eof",
		Key{Tab, 0}:     "start-completion",
		Key{PageUp, 0}:  "start-history",
		Key{'N', Ctrl}:  "start-navigation",
		DefaultBinding:  "default-insert",
	},
	modeCompletion: map[Key]string{
		Key{'[', Ctrl}: "cancel-completion",
		Key{Up, 0}:     "select-cand-up",
		Key{Down, 0}:   "select-cand-down",
		Key{Left, 0}:   "select-cand-left",
		Key{Right, 0}:  "select-cand-right",
		// Key{Tab, 0}:    "cycle-cand-right",
		DefaultBinding: "default-completion",
	},
	modeNavigation: map[Key]string{
		Key{Up, 0}:     "select-nav-up",
		Key{Down, 0}:   "select-nav-down",
		Key{Left, 0}:   "ascend-nav",
		Key{Right, 0}:  "descend-nav",
		DefaultBinding: "default-navigation",
	},
	modeHistory: map[Key]string{
		Key{'[', Ctrl}:   "start-insert",
		Key{PageUp, 0}:   "select-history-prev",
		Key{PageDown, 0}: "select-history-next",
		DefaultBinding:   "default-history",
	},
}

func init() {
	for _, kb := range keyBindings {
		for _, name := range kb {
			if builtins[name] == nil {
				panic("bad keyBindings table: no editor builtin named " + name)
			}
		}
	}
}

// acceptCompletion accepts currently selected completion candidate.
func (ed *Editor) acceptCompletion() {
	c := ed.completion
	if 0 <= c.current && c.current < len(c.candidates) {
		accepted := c.candidates[c.current].source.text
		// Insert the accepted completion text at ed.dot and move ed.dot after
		// the newly inserted text
		ed.line = ed.line[:ed.dot] + accepted + ed.line[ed.dot:]
		ed.dot += len(accepted)
	}
	ed.completion = nil
	ed.mode = modeInsert
}

// acceptHistory accepts currently history.
func (ed *Editor) acceptHistory() {
	ed.line = ed.history.line
	ed.dot = len(ed.line)
}

func setupTerminal(file *os.File) (*sys.Termios, error) {
	fd := int(file.Fd())
	term, err := sys.NewTermiosFromFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't get terminal attribute: %s", err)
	}

	savedTermios := term.Copy()

	term.SetICanon(false)
	term.SetEcho(false)
	term.SetVMin(1)
	term.SetVTime(0)

	err = term.ApplyToFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't set up terminal attribute: %s", err)
	}

	err = sys.FlushInput(fd)
	if err != nil {
		return nil, fmt.Errorf("can't flush input: %s", err)
	}

	return savedTermios, nil
}

// startsReadLine prepares the terminal for the editor.
func (ed *Editor) startReadLine() error {
	savedTermios, err := setupTerminal(ed.file)
	if err != nil {
		return err
	}
	ed.savedTermios = savedTermios

	_, width := sys.GetWinsize(int(ed.file.Fd()))
	// Turn on autowrap, write lackEOL along with enough padding to fill the
	// whole screen. If the cursor was in the first column, we end up in the
	// same line (just off the line boundary); otherwise we are now in the next
	// line. We now rewind to the first column and erase anything there. The
	// final effect is that a lackEOL gets written if and only if the cursor
	// was not in the first column.
	//
	// After that, we turn off autowrap. The editor has its own wrapping
	// machanism.
	fmt.Fprintf(ed.file, "\033[?7h%s%*s\r \r\033[?7l", lackEOL, width-WcWidth(lackEOLRune), "")

	return nil
}

// finishReadLine puts the terminal in a state suitable for other programs to
// use.
func (ed *Editor) finishReadLine(lr *LineRead) {
	if lr.EOF == false && lr.Err == nil && lr.Line != "" {
		ed.appendHistory(lr.Line)
	}

	ed.mode = modeInsert
	ed.tips = nil
	ed.completion = nil
	ed.navigation = nil
	ed.dot = len(ed.line)
	// TODO Perhaps make it optional to NOT clear the rprompt
	ed.rprompt = ""
	ed.refresh() // XXX(xiaq): Ignore possible error
	ed.file.WriteString("\n")

	ed.reader.Stop()

	// turn on autowrap
	ed.file.WriteString("\033[?7h")

	// restore termios
	err := ed.savedTermios.ApplyToFd(int(ed.file.Fd()))

	if err != nil {
		// BUG(xiaq): Error in Editor.finishReadLine may override earlier error
		*lr = LineRead{Err: fmt.Errorf("can't restore terminal attribute: %s", err)}
	}
	ed.savedTermios = nil
	ed.editorState = editorState{}
}

// ReadLine reads a line interactively.
// TODO(xiaq): ReadLine currently handles SIGINT and SIGWINCH and swallows all
// other signals.
func (ed *Editor) ReadLine(prompt, rprompt func() string) (lr LineRead) {
	ed.editorState = editorState{active: true}
	go ed.updateIsExternal()

	ed.writer.oldBuf.cells = nil
	ones := ed.reader.Chan()

	err := ed.startReadLine()
	if err != nil {
		return LineRead{Err: err}
	}
	defer ed.finishReadLine(&lr)

MainLoop:
	for {
		ed.prompt = prompt()
		ed.rprompt = rprompt()

		err := ed.refresh()
		if err != nil {
			return LineRead{Err: err}
		}

		ed.tips = nil

		select {
		case sig := <-ed.sigs:
			// TODO(xiaq): Maybe support customizable handling of signals
			switch sig {
			case syscall.SIGINT:
				// Start over
				ed.editorState = editorState{savedTermios: ed.savedTermios}
				goto MainLoop
			case syscall.SIGWINCH:
				continue MainLoop
			}
		case or := <-ones:
			// Alert about error
			err := or.Err
			if err != nil {
				ed.pushTip(err.Error())
				continue
			}

			// Ignore bogus CPR
			if or.CPR != invalidPos {
				continue
			}

			k := or.Key
		lookupKey:
			keyBinding, ok := keyBindings[ed.mode]
			if !ok {
				ed.pushTip("No binding for current mode")
				continue
			}

			name, bound := keyBinding[k]
			if !bound {
				name = keyBinding[DefaultBinding]
			}

			ed.lastKey = k
			builtins[name](ed)
			act := ed.nextAction
			ed.nextAction = action{}

			switch act.actionType {
			case noAction:
				continue
			case reprocessKey:
				err = ed.refresh()
				if err != nil {
					return LineRead{Err: err}
				}
				goto lookupKey
			case exitReadLine:
				return act.returnValue
			}
		}
	}
}
<|endoftext|>


<commit_before>// mkbundle is a commandline tool for building certificate pool bundles.
// All certificates in the input file paths are checked for revocation and bundled together.
//
// Usage:
//	mkbundle -f bundle_file -nw number_of_workers certificate_file_path ...
package main

import (
	"crypto/x509"
	"encoding/pem"
	"flag"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/cloudflare/cfssl/log"
	"github.com/cloudflare/cfssl/revoke"
)

// worker does all the parsing and validation of the certificate(s)
// contained in a single file. It first reads all the data in the
// file, then begins parsing certificates in the file. Those
// certificates are then checked for revocation.
func worker(paths chan string, bundler chan *x509.Certificate, pool *sync.WaitGroup) {
	defer (*pool).Done()
	for {
		path, ok := <-paths
		if !ok {
			return
		}

		log.Infof("Loading %s", path)

		fileData, err := ioutil.ReadFile(path)
		if err != nil {
			log.Warningf("%v", err)
			continue
		}

		for {
			var block *pem.Block
			if len(fileData) == 0 {
				break
			}
			block, fileData = pem.Decode(fileData)
			if block == nil {
				log.Warningf("%s: no PEM data found", path)
				break
			} else if block.Type != "CERTIFICATE" {
				log.Info("Skipping non-certificate")
				continue
			}

			cert, err := x509.ParseCertificate(block.Bytes)
			if err != nil {
				log.Warningf("Invalid certificate: %v", err)
				continue
			}

			log.Infof("Validating %+v", cert.Subject)
			revoked, ok := revoke.VerifyCertificate(cert)
			if !ok {
				log.Warning("Failed to verify certificate.")
			} else if !revoked {
				bundler <- cert
			} else {
				log.Info("Skipping revoked certificate")
			}
		}
	}
}

// supervisor sets up the workers and signals the bundler that all
// certificates have been processed.
func supervisor(paths chan string, bundler chan *x509.Certificate, numWorkers int) {
	var workerPool sync.WaitGroup
	for i := 0; i < numWorkers; i++ {
		workerPool.Add(1)
		go worker(paths, bundler, &workerPool)
	}
	workerPool.Wait()
	close(bundler)
}

// makeBundle opens the file for writing, and listens for incoming
// certificates. These are PEM-encoded and written to file.
func makeBundle(filename string, bundler chan *x509.Certificate) {
	file, err := os.Create(filename)
	if err != nil {
		log.Errorf("%v", err)
		return
	}
	defer file.Close()

	var total int
	for {
		cert, ok := <-bundler
		if !ok {
			break
		}
		block := &pem.Block{
			Type:  "CERTIFICATE",
			Bytes: cert.Raw,
		}
		err = pem.Encode(file, block)
		if err != nil {
			log.Errorf("Failed to write PEM block: %v", err)
			break
		}
		total++
	}
	log.Infof("Wrote %d certificates.", total)
}

// scanFiles walks the files listed in the arguments. These files may
// be either certificate files or directories containing certificates.
func scanFiles(paths chan string) {
	walker := func(path string, info os.FileInfo, err error) error {
		log.Infof("Found %s", path)
		if err != nil {
			return err
		}

		if info.Mode().IsRegular() {
			paths <- path
		}
		return nil
	}

	for _, path := range flag.Args() {
		err := filepath.Walk(path, walker)
		if err != nil {
			log.Errorf("Walk failed: %vf", err)
		}
	}
	close(paths)
}

func main() {
	logLevel := flag.Int("loglevel", log.LevelWarning, "verbosity of logs (0-5, 0 is very noisy)")
	bundleFile := flag.String("f", "cert-bundle.crt", "path to store certificate bundle")
	numWorkers := flag.Int("nw", 4, "number of workers")
	flag.Parse()

	log.Level = *logLevel

	paths := make(chan string)
	bundler := make(chan *x509.Certificate)

	go supervisor(paths, bundler, *numWorkers)
	go scanFiles(paths)

	makeBundle(*bundleFile, bundler)
}
<commit_msg>remove extra f<commit_after>// mkbundle is a commandline tool for building certificate pool bundles.
// All certificates in the input file paths are checked for revocation and bundled together.
//
// Usage:
//	mkbundle -f bundle_file -nw number_of_workers certificate_file_path ...
package main

import (
	"crypto/x509"
	"encoding/pem"
	"flag"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/cloudflare/cfssl/log"
	"github.com/cloudflare/cfssl/revoke"
)

// worker does all the parsing and validation of the certificate(s)
// contained in a single file. It first reads all the data in the
// file, then begins parsing certificates in the file. Those
// certificates are then checked for revocation.
func worker(paths chan string, bundler chan *x509.Certificate, pool *sync.WaitGroup) {
	defer (*pool).Done()
	for {
		path, ok := <-paths
		if !ok {
			return
		}

		log.Infof("Loading %s", path)

		fileData, err := ioutil.ReadFile(path)
		if err != nil {
			log.Warningf("%v", err)
			continue
		}

		for {
			var block *pem.Block
			if len(fileData) == 0 {
				break
			}
			block, fileData = pem.Decode(fileData)
			if block == nil {
				log.Warningf("%s: no PEM data found", path)
				break
			} else if block.Type != "CERTIFICATE" {
				log.Info("Skipping non-certificate")
				continue
			}

			cert, err := x509.ParseCertificate(block.Bytes)
			if err != nil {
				log.Warningf("Invalid certificate: %v", err)
				continue
			}

			log.Infof("Validating %+v", cert.Subject)
			revoked, ok := revoke.VerifyCertificate(cert)
			if !ok {
				log.Warning("Failed to verify certificate.")
			} else if !revoked {
				bundler <- cert
			} else {
				log.Info("Skipping revoked certificate")
			}
		}
	}
}

// supervisor sets up the workers and signals the bundler that all
// certificates have been processed.
func supervisor(paths chan string, bundler chan *x509.Certificate, numWorkers int) {
	var workerPool sync.WaitGroup
	for i := 0; i < numWorkers; i++ {
		workerPool.Add(1)
		go worker(paths, bundler, &workerPool)
	}
	workerPool.Wait()
	close(bundler)
}

// makeBundle opens the file for writing, and listens for incoming
// certificates. These are PEM-encoded and written to file.
func makeBundle(filename string, bundler chan *x509.Certificate) {
	file, err := os.Create(filename)
	if err != nil {
		log.Errorf("%v", err)
		return
	}
	defer file.Close()

	var total int
	for {
		cert, ok := <-bundler
		if !ok {
			break
		}
		block := &pem.Block{
			Type:  "CERTIFICATE",
			Bytes: cert.Raw,
		}
		err = pem.Encode(file, block)
		if err != nil {
			log.Errorf("Failed to write PEM block: %v", err)
			break
		}
		total++
	}
	log.Infof("Wrote %d certificates.", total)
}

// scanFiles walks the files listed in the arguments. These files may
// be either certificate files or directories containing certificates.
func scanFiles(paths chan string) {
	walker := func(path string, info os.FileInfo, err error) error {
		log.Infof("Found %s", path)
		if err != nil {
			return err
		}

		if info.Mode().IsRegular() {
			paths <- path
		}
		return nil
	}

	for _, path := range flag.Args() {
		err := filepath.Walk(path, walker)
		if err != nil {
			log.Errorf("Walk failed: %v", err)
		}
	}
	close(paths)
}

func main() {
	logLevel := flag.Int("loglevel", log.LevelWarning, "verbosity of logs (0-5, 0 is very noisy)")
	bundleFile := flag.String("f", "cert-bundle.crt", "path to store certificate bundle")
	numWorkers := flag.Int("nw", 4, "number of workers")
	flag.Parse()

	log.Level = *logLevel

	paths := make(chan string)
	bundler := make(chan *x509.Certificate)

	go supervisor(paths, bundler, *numWorkers)
	go scanFiles(paths)

	makeBundle(*bundleFile, bundler)
}
<|endoftext|>


<commit_before>package mnemosyned

import (
	"database/sql"
	"errors"
	"io"
	"net"
	"net/http"
	"net/http/pprof"
	"net/url"
	"strings"
	"testing"
	"time"

	"context"

	"github.com/piotrkowalczuk/mnemosyne/internal/cache"
	"github.com/piotrkowalczuk/mnemosyne/internal/cluster"
	"github.com/piotrkowalczuk/mnemosyne/internal/service/postgres"
	"github.com/piotrkowalczuk/mnemosyne/internal/storage"
	storagepq "github.com/piotrkowalczuk/mnemosyne/internal/storage/postgres"
	"github.com/piotrkowalczuk/mnemosyne/mnemosynerpc"
	"github.com/piotrkowalczuk/promgrpc"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/health"
	"google.golang.org/grpc/health/grpc_health_v1"
)

const subsystem = "mnemosyned"

// DaemonOpts it is constructor argument that can be passed to
// the NewDaemon constructor function.
type DaemonOpts struct {
	Version           string
	IsTest            bool
	SessionTTL        time.Duration
	SessionTTC        time.Duration
	TLS               bool
	TLSCertFile       string
	TLSKeyFile        string
	Storage           string
	PostgresAddress   string
	PostgresTable     string
	PostgresSchema    string
	Logger            *zap.Logger
	RPCOptions        []grpc.ServerOption
	RPCListener       net.Listener
	DebugListener     net.Listener
	ClusterListenAddr string
	ClusterSeeds      []string
}

// TestDaemonOpts set of options that are used with TestDaemon instance.
type TestDaemonOpts struct {
	StoragePostgresAddress string
}

// Daemon represents single daemon instance that can be run.
type Daemon struct {
	opts          *DaemonOpts
	done          chan struct{}
	serverOptions []grpc.ServerOption
	clientOptions []grpc.DialOption
	postgres      *sql.DB
	logger        *zap.Logger
	server        *grpc.Server
	storage       storage.Storage
	rpcListener   net.Listener
	debugListener net.Listener
}

// NewDaemon allocates new daemon instance using given options.
func NewDaemon(opts *DaemonOpts) (*Daemon, error) {
	d := &Daemon{
		done:          make(chan struct{}),
		opts:          opts,
		logger:        opts.Logger,
		serverOptions: opts.RPCOptions,
		rpcListener:   opts.RPCListener,
		debugListener: opts.DebugListener,
	}

	if err := d.setPostgresConnectionParameters(); err != nil {
		return nil, err
	}
	if d.opts.SessionTTL == 0 {
		d.opts.SessionTTL = storage.DefaultTTL
	}
	if d.opts.SessionTTC == 0 {
		d.opts.SessionTTC = storage.DefaultTTC
	}
	if d.opts.Storage == "" {
		d.opts.Storage = storage.EnginePostgres
	}
	if d.opts.PostgresTable == "" {
		d.opts.PostgresTable = "session"
	}
	if d.opts.PostgresSchema == "" {
		d.opts.PostgresSchema = "mnemosyne"
	}

	return d, nil
}

// TestDaemon returns address of fully started in-memory daemon and closer to close it.
func TestDaemon(t *testing.T, opts TestDaemonOpts) (net.Addr, io.Closer) {
	l, err := net.Listen("tcp", "127.0.0.1:0") // any available address
	if err != nil {
		t.Fatalf("mnemosyne daemon tcp listener setup error: %s", err.Error())
	}

	d, err := NewDaemon(&DaemonOpts{
		IsTest:            true,
		ClusterListenAddr: l.Addr().String(),
		Logger:            zap.L(),
		PostgresAddress:   opts.StoragePostgresAddress,
		PostgresTable:     "session",
		PostgresSchema:    "mnemosyne",
		RPCListener:       l,
	})
	if err != nil {
		t.Fatalf("mnemosyne daemon cannot be instantiated: %s", err.Error())
	}
	if err := d.Run(); err != nil {
		t.Fatalf("mnemosyne daemon start error: %s", err.Error())
	}

	return d.Addr(), d
}

// Run starts daemon and all services within.
func (d *Daemon) Run() (err error) {
	var (
		cl *cluster.Cluster
	)
	if cl, err = initCluster(d.logger, d.opts.ClusterListenAddr, d.opts.ClusterSeeds...); err != nil {
		return
	}
	if err = d.initStorage(d.logger, d.opts.PostgresTable, d.opts.PostgresSchema); err != nil {
		return
	}

	interceptor := promgrpc.NewInterceptor(promgrpc.InterceptorOpts{})

	d.clientOptions = []grpc.DialOption{
		grpc.WithUserAgent(subsystem),
		grpc.WithStatsHandler(interceptor),
		grpc.WithDialer(interceptor.Dialer(func(addr string, timeout time.Duration) (net.Conn, error) {
			return net.DialTimeout("tcp", addr, timeout)
		})),
		grpc.WithUnaryInterceptor(interceptor.UnaryClient()),
		grpc.WithStreamInterceptor(interceptor.StreamClient()),
	}
	d.serverOptions = []grpc.ServerOption{
		grpc.StatsHandler(interceptor),
		grpc.UnaryInterceptor(unaryServerInterceptors(
			errorInterceptor(d.logger),
			interceptor.UnaryServer(),
		)),
	}
	if d.opts.TLS {
		servCreds, err := credentials.NewServerTLSFromFile(d.opts.TLSCertFile, d.opts.TLSKeyFile)
		if err != nil {
			return err
		}
		d.serverOptions = append(d.serverOptions, grpc.Creds(servCreds))

		clientCreds, err := credentials.NewClientTLSFromFile(d.opts.TLSCertFile, "")
		if err != nil {
			return err
		}
		d.clientOptions = append(d.clientOptions, grpc.WithTransportCredentials(clientCreds))
	} else {
		d.clientOptions = append(d.clientOptions, grpc.WithInsecure())
	}

	d.server = grpc.NewServer(d.serverOptions...)

	cache := cache.New(5*time.Second, subsystem)
	mnemosyneServer, err := newSessionManager(sessionManagerOpts{
		addr:    d.opts.ClusterListenAddr,
		cluster: cl,
		logger:  d.logger,
		storage: d.storage,
		ttc:     d.opts.SessionTTC,
		cache:   cache,
	})
	if err != nil {
		return err
	}

	mnemosynerpc.RegisterSessionManagerServer(d.server, mnemosyneServer)
	grpc_health_v1.RegisterHealthServer(d.server, health.NewServer())

	if !d.opts.IsTest {
		prometheus.DefaultRegisterer.Register(d.storage.(storage.InstrumentedStorage))
		prometheus.DefaultRegisterer.Register(cache)
		prometheus.DefaultRegisterer.Register(mnemosyneServer)
		prometheus.DefaultRegisterer.Register(interceptor)
		promgrpc.RegisterInterceptor(d.server, interceptor)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if err = cl.Connect(ctx, d.clientOptions...); err != nil {
		return err
	}

	go func() {
		d.logger.Info("rpc server is running", zap.String("address", d.rpcListener.Addr().String()))

		if err := d.server.Serve(d.rpcListener); err != nil {
			if err == grpc.ErrServerStopped {
				d.logger.Info("grpc server has been stopped")
				return
			}

			if !strings.Contains(err.Error(), "use of closed network connection") {
				d.logger.Error("rpc server failure", zap.Error(err))
			}
		}
	}()

	if d.debugListener != nil {
		go func() {
			d.logger.Info("debug server is running", zap.String("address", d.debugListener.Addr().String()))

			mux := http.NewServeMux()
			mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
			mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
			mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
			mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
			mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
			mux.Handle("/metrics", promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{}))
			mux.Handle("/healthz", &livenessHandler{
				livenessResponse: livenessResponse{
					Version: d.opts.Version,
				},
				logger: d.logger,
			})
			mux.Handle("/healthr", &readinessHandler{
				livenessResponse: livenessResponse{
					Version: d.opts.Version,
				},
				logger:   d.logger,
				postgres: d.postgres,
				cluster:  cl,
			})
			if err := http.Serve(d.debugListener, mux); err != nil {
				d.logger.Error("debug server failure", zap.Error(err))
			}
		}()
	}

	go mnemosyneServer.cleanup(d.done)

	return
}

// Close implements io.Closer interface.
func (d *Daemon) Close() (err error) {
	d.done <- struct{}{}
	d.server.GracefulStop()
	if d.postgres != nil {
		if err = d.postgres.Close(); err != nil {
			return
		}
	}
	if d.debugListener != nil {
		err = d.debugListener.Close()
	}

	return
}

// Addr returns net.Addr that rpc service is listening on.
func (d *Daemon) Addr() net.Addr {
	return d.rpcListener.Addr()
}

func (d *Daemon) initStorage(l *zap.Logger, table, schema string) (err error) {
	switch d.opts.Storage {
	case storage.EngineInMemory:
		return errors.New("in memory storage is not implemented yet")
	case storage.EnginePostgres:
		d.postgres, err = postgres.Init(
			d.opts.PostgresAddress,
			postgres.Opts{
				Logger: d.logger,
			},
		)
		if err != nil {
			return
		}
		if d.storage, err = storage.Init(storagepq.NewStorage(storagepq.StorageOpts{
			Namespace: subsystem,
			Schema:    schema,
			Table:     table,
			Conn:      d.postgres,
			TTL:       d.opts.SessionTTL,
		}), d.opts.IsTest); err != nil {
			return
		}

		l.Info("postgres storage initialized", zap.String("schema", schema), zap.String("table", table))
		return
	case storage.EngineRedis:
		return errors.New("redis storage is not implemented yet")
	default:
		return errors.New("unknown storage engine")
	}
}

func (d *Daemon) setPostgresConnectionParameters() error {
	u, err := url.Parse(d.opts.PostgresAddress)
	if err != nil {
		return err
	}
	v := u.Query()
	v.Set("timezone", "utc")
	u.RawQuery = v.Encode()
	d.opts.PostgresAddress = u.String()
	return nil
}
<commit_msg>grpc user agent has service version as well<commit_after>package mnemosyned

import (
	"database/sql"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/pprof"
	"net/url"
	"strings"
	"testing"
	"time"

	"context"

	"github.com/piotrkowalczuk/mnemosyne/internal/cache"
	"github.com/piotrkowalczuk/mnemosyne/internal/cluster"
	"github.com/piotrkowalczuk/mnemosyne/internal/service/postgres"
	"github.com/piotrkowalczuk/mnemosyne/internal/storage"
	storagepq "github.com/piotrkowalczuk/mnemosyne/internal/storage/postgres"
	"github.com/piotrkowalczuk/mnemosyne/mnemosynerpc"
	"github.com/piotrkowalczuk/promgrpc"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/health"
	"google.golang.org/grpc/health/grpc_health_v1"
)

const subsystem = "mnemosyned"

// DaemonOpts it is constructor argument that can be passed to
// the NewDaemon constructor function.
type DaemonOpts struct {
	Version           string
	IsTest            bool
	SessionTTL        time.Duration
	SessionTTC        time.Duration
	TLS               bool
	TLSCertFile       string
	TLSKeyFile        string
	Storage           string
	PostgresAddress   string
	PostgresTable     string
	PostgresSchema    string
	Logger            *zap.Logger
	RPCOptions        []grpc.ServerOption
	RPCListener       net.Listener
	DebugListener     net.Listener
	ClusterListenAddr string
	ClusterSeeds      []string
}

// TestDaemonOpts set of options that are used with TestDaemon instance.
type TestDaemonOpts struct {
	StoragePostgresAddress string
}

// Daemon represents single daemon instance that can be run.
type Daemon struct {
	opts          *DaemonOpts
	done          chan struct{}
	serverOptions []grpc.ServerOption
	clientOptions []grpc.DialOption
	postgres      *sql.DB
	logger        *zap.Logger
	server        *grpc.Server
	storage       storage.Storage
	rpcListener   net.Listener
	debugListener net.Listener
}

// NewDaemon allocates new daemon instance using given options.
func NewDaemon(opts *DaemonOpts) (*Daemon, error) {
	d := &Daemon{
		done:          make(chan struct{}),
		opts:          opts,
		logger:        opts.Logger,
		serverOptions: opts.RPCOptions,
		rpcListener:   opts.RPCListener,
		debugListener: opts.DebugListener,
	}

	if err := d.setPostgresConnectionParameters(); err != nil {
		return nil, err
	}
	if d.opts.SessionTTL == 0 {
		d.opts.SessionTTL = storage.DefaultTTL
	}
	if d.opts.SessionTTC == 0 {
		d.opts.SessionTTC = storage.DefaultTTC
	}
	if d.opts.Storage == "" {
		d.opts.Storage = storage.EnginePostgres
	}
	if d.opts.PostgresTable == "" {
		d.opts.PostgresTable = "session"
	}
	if d.opts.PostgresSchema == "" {
		d.opts.PostgresSchema = "mnemosyne"
	}

	return d, nil
}

// TestDaemon returns address of fully started in-memory daemon and closer to close it.
func TestDaemon(t *testing.T, opts TestDaemonOpts) (net.Addr, io.Closer) {
	l, err := net.Listen("tcp", "127.0.0.1:0") // any available address
	if err != nil {
		t.Fatalf("mnemosyne daemon tcp listener setup error: %s", err.Error())
	}

	d, err := NewDaemon(&DaemonOpts{
		IsTest:            true,
		ClusterListenAddr: l.Addr().String(),
		Logger:            zap.L(),
		PostgresAddress:   opts.StoragePostgresAddress,
		PostgresTable:     "session",
		PostgresSchema:    "mnemosyne",
		RPCListener:       l,
	})
	if err != nil {
		t.Fatalf("mnemosyne daemon cannot be instantiated: %s", err.Error())
	}
	if err := d.Run(); err != nil {
		t.Fatalf("mnemosyne daemon start error: %s", err.Error())
	}

	return d.Addr(), d
}

// Run starts daemon and all services within.
func (d *Daemon) Run() (err error) {
	var (
		cl *cluster.Cluster
	)
	if cl, err = initCluster(d.logger, d.opts.ClusterListenAddr, d.opts.ClusterSeeds...); err != nil {
		return
	}
	if err = d.initStorage(d.logger, d.opts.PostgresTable, d.opts.PostgresSchema); err != nil {
		return
	}

	interceptor := promgrpc.NewInterceptor(promgrpc.InterceptorOpts{})

	d.clientOptions = []grpc.DialOption{
		grpc.WithUserAgent(fmt.Sprintf("%s:%s", subsystem, d.opts.Version)),
		grpc.WithStatsHandler(interceptor),
		grpc.WithDialer(interceptor.Dialer(func(addr string, timeout time.Duration) (net.Conn, error) {
			return net.DialTimeout("tcp", addr, timeout)
		})),
		grpc.WithUnaryInterceptor(interceptor.UnaryClient()),
		grpc.WithStreamInterceptor(interceptor.StreamClient()),
	}
	d.serverOptions = []grpc.ServerOption{
		grpc.StatsHandler(interceptor),
		grpc.UnaryInterceptor(unaryServerInterceptors(
			errorInterceptor(d.logger),
			interceptor.UnaryServer(),
		)),
	}
	if d.opts.TLS {
		servCreds, err := credentials.NewServerTLSFromFile(d.opts.TLSCertFile, d.opts.TLSKeyFile)
		if err != nil {
			return err
		}
		d.serverOptions = append(d.serverOptions, grpc.Creds(servCreds))

		clientCreds, err := credentials.NewClientTLSFromFile(d.opts.TLSCertFile, "")
		if err != nil {
			return err
		}
		d.clientOptions = append(d.clientOptions, grpc.WithTransportCredentials(clientCreds))
	} else {
		d.clientOptions = append(d.clientOptions, grpc.WithInsecure())
	}

	d.server = grpc.NewServer(d.serverOptions...)

	cache := cache.New(5*time.Second, subsystem)
	mnemosyneServer, err := newSessionManager(sessionManagerOpts{
		addr:    d.opts.ClusterListenAddr,
		cluster: cl,
		logger:  d.logger,
		storage: d.storage,
		ttc:     d.opts.SessionTTC,
		cache:   cache,
	})
	if err != nil {
		return err
	}

	mnemosynerpc.RegisterSessionManagerServer(d.server, mnemosyneServer)
	grpc_health_v1.RegisterHealthServer(d.server, health.NewServer())

	if !d.opts.IsTest {
		prometheus.DefaultRegisterer.Register(d.storage.(storage.InstrumentedStorage))
		prometheus.DefaultRegisterer.Register(cache)
		prometheus.DefaultRegisterer.Register(mnemosyneServer)
		prometheus.DefaultRegisterer.Register(interceptor)
		promgrpc.RegisterInterceptor(d.server, interceptor)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if err = cl.Connect(ctx, d.clientOptions...); err != nil {
		return err
	}

	go func() {
		d.logger.Info("rpc server is running", zap.String("address", d.rpcListener.Addr().String()))

		if err := d.server.Serve(d.rpcListener); err != nil {
			if err == grpc.ErrServerStopped {
				d.logger.Info("grpc server has been stopped")
				return
			}

			if !strings.Contains(err.Error(), "use of closed network connection") {
				d.logger.Error("rpc server failure", zap.Error(err))
			}
		}
	}()

	if d.debugListener != nil {
		go func() {
			d.logger.Info("debug server is running", zap.String("address", d.debugListener.Addr().String()))

			mux := http.NewServeMux()
			mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
			mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
			mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
			mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
			mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
			mux.Handle("/metrics", promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{}))
			mux.Handle("/healthz", &livenessHandler{
				livenessResponse: livenessResponse{
					Version: d.opts.Version,
				},
				logger: d.logger,
			})
			mux.Handle("/healthr", &readinessHandler{
				livenessResponse: livenessResponse{
					Version: d.opts.Version,
				},
				logger:   d.logger,
				postgres: d.postgres,
				cluster:  cl,
			})
			if err := http.Serve(d.debugListener, mux); err != nil {
				d.logger.Error("debug server failure", zap.Error(err))
			}
		}()
	}

	go mnemosyneServer.cleanup(d.done)

	return
}

// Close implements io.Closer interface.
func (d *Daemon) Close() (err error) {
	d.done <- struct{}{}
	d.server.GracefulStop()
	if d.postgres != nil {
		if err = d.postgres.Close(); err != nil {
			return
		}
	}
	if d.debugListener != nil {
		err = d.debugListener.Close()
	}

	return
}

// Addr returns net.Addr that rpc service is listening on.
func (d *Daemon) Addr() net.Addr {
	return d.rpcListener.Addr()
}

func (d *Daemon) initStorage(l *zap.Logger, table, schema string) (err error) {
	switch d.opts.Storage {
	case storage.EngineInMemory:
		return errors.New("in memory storage is not implemented yet")
	case storage.EnginePostgres:
		d.postgres, err = postgres.Init(
			d.opts.PostgresAddress,
			postgres.Opts{
				Logger: d.logger,
			},
		)
		if err != nil {
			return
		}
		if d.storage, err = storage.Init(storagepq.NewStorage(storagepq.StorageOpts{
			Namespace: subsystem,
			Schema:    schema,
			Table:     table,
			Conn:      d.postgres,
			TTL:       d.opts.SessionTTL,
		}), d.opts.IsTest); err != nil {
			return
		}

		l.Info("postgres storage initialized", zap.String("schema", schema), zap.String("table", table))
		return
	case storage.EngineRedis:
		return errors.New("redis storage is not implemented yet")
	default:
		return errors.New("unknown storage engine")
	}
}

func (d *Daemon) setPostgresConnectionParameters() error {
	u, err := url.Parse(d.opts.PostgresAddress)
	if err != nil {
		return err
	}
	v := u.Query()
	v.Set("timezone", "utc")
	u.RawQuery = v.Encode()
	d.opts.PostgresAddress = u.String()
	return nil
}
<|endoftext|>


<commit_before>package mock_service_test

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/wchan2/mock_service"
)

func TestServeEndpointRegistration_NilReqBody(t *testing.T) {
	service := mock_service.New("/mocks")
	req, err := http.NewRequest(http.MethodPost, "/mocks", nil)
	if err != nil {
		t.Fatalf("Expected error to create new request to be nil but got %s", err)
	}

	recorder := httptest.NewRecorder()
	service.ServeHTTP(recorder, req)
	if recorder.Result().StatusCode != http.StatusBadRequest {
		t.Errorf("Expected %d but received %d", http.StatusBadRequest, recorder.Result().StatusCode)
	}

	if recorder.Body.String() != "Registering an endpoint requires a payload" {
		t.Errorf(
			`Expected message: "%s" but received "%s"`,
			"Registering an endpoint requires a payload",
			recorder.Body.String(),
		)
	}
}

func TestServeEndpointRegistration_InvalidJSONReqBody(t *testing.T) {
	service := mock_service.New("/mocks")
	req, err := http.NewRequest(http.MethodPost, "/mocks", strings.NewReader(""))
	if err != nil {
		t.Fatalf("Expected error to create new request to be nil but got %s", err)
	}

	recorder := httptest.NewRecorder()
	service.ServeHTTP(recorder, req)
	if recorder.Result().StatusCode != http.StatusInternalServerError {
		t.Errorf("Expected %d but received %d", http.StatusBadRequest, recorder.Result().StatusCode)
	}

	if recorder.Body.String() != "Unable to Unmarshal request body : unexpected end of JSON input" {
		t.Errorf(
			`Expected message: "%s" but received "%s"`,
			"Unable to Unmarshal request body : unexpected end of JSON input",
			recorder.Body.String(),
		)
	}
}

func TestServeEndpointRegistration_Success(t *testing.T) {
	service := mock_service.New("/mocks")
	const jsonRequestBody = `{"method": "GET", "endpoint": "/mock/test", "httpStatusCode": 201, "responseBody": "hello world", "responseHeaders": {"Foo": "Bar"}}`
	req, err := http.NewRequest(http.MethodPost, "/mocks", strings.NewReader(jsonRequestBody))
	if err != nil {
		t.Fatalf("Expected error to create new request to be nil but got %s", err)
	}

	recorder := httptest.NewRecorder()
	service.ServeHTTP(recorder, req)
	if recorder.Result().StatusCode != http.StatusCreated {
		t.Errorf("Expected %i status but got %i", http.StatusCreated, recorder.Result().StatusCode)
	}
}
<commit_msg>add test for serving a mock response<commit_after>package mock_service_test

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/wchan2/mock_service"
)

const successfulRegistrationRequest = `{"method": "GET", "endpoint": "/mock/test", "httpStatusCode": 203, "responseBody": "hello world", "responseHeaders": {"Foo": "Bar"}}`

func TestServeEndpointRegistration_NilReqBody(t *testing.T) {
	service := mock_service.New("/mocks")
	req, err := http.NewRequest(http.MethodPost, "/mocks", nil)
	if err != nil {
		t.Fatalf("Expected error to create new request to be nil but got %s", err)
	}

	recorder := httptest.NewRecorder()
	service.ServeHTTP(recorder, req)
	if recorder.Code != http.StatusBadRequest {
		t.Errorf("Expected %d but received %d", http.StatusBadRequest, recorder.Code)
	}

	if recorder.Body.String() != "Registering an endpoint requires a payload" {
		t.Errorf(
			`Expected message: "%s" but received "%s"`,
			"Registering an endpoint requires a payload",
			recorder.Body.String(),
		)
	}
}

func TestServeEndpointRegistration_InvalidJSONReqBody(t *testing.T) {
	service := mock_service.New("/mocks")
	req, err := http.NewRequest(http.MethodPost, "/mocks", strings.NewReader(""))
	if err != nil {
		t.Fatalf("Expected error to create new request to be nil but got %s", err)
	}

	recorder := httptest.NewRecorder()
	service.ServeHTTP(recorder, req)
	if recorder.Code != http.StatusInternalServerError {
		t.Errorf("Expected %d but received %d", http.StatusBadRequest, recorder.Code)
	}

	if recorder.Body.String() != "Unable to Unmarshal request body : unexpected end of JSON input" {
		t.Errorf(
			`Expected message: "%s" but received "%s"`,
			"Unable to Unmarshal request body : unexpected end of JSON input",
			recorder.Body.String(),
		)
	}
}

func TestServeEndpointRegistration_Success(t *testing.T) {
	service := mock_service.New("/mocks")
	req, err := http.NewRequest(http.MethodPost, "/mocks", strings.NewReader(successfulRegistrationRequest))
	if err != nil {
		t.Fatalf("Expected error to create new request to be nil but got %s", err)
	}

	recorder := httptest.NewRecorder()
	service.ServeHTTP(recorder, req)
	if recorder.Code != http.StatusCreated {
		t.Errorf("Expected %i status but got %i", http.StatusCreated, recorder.Code)
	}
}
func TestServeMockHTTP(t *testing.T) {
	service := mock_service.New("/mocks")
	req, err := http.NewRequest(http.MethodPost, "/mocks", strings.NewReader(successfulRegistrationRequest))
	if err != nil {
		t.Fatalf("Expected error to create new request to be nil but got %s", err)
	}

	recorder := httptest.NewRecorder()
	service.ServeHTTP(recorder, req)
	if recorder.Code != http.StatusCreated {
		t.Errorf("Expected %d status but got %d when registering the mock endpoint", http.StatusCreated, recorder.Code)
	}

	// test the mock endpoint
	testReq, err := http.NewRequest("GET", "/mock/test", nil)
	if err != nil {
		t.Fatalf("Expected error to create new request ot be nil but got %s", err)
	}
	testRecorder := httptest.NewRecorder()
	service.ServeHTTP(testRecorder, testReq)
	if testRecorder.Code != http.StatusNonAuthoritativeInfo {
		t.Errorf("Expected %d status but got %d when sending a mock request", http.StatusNonAuthoritativeInfo, testRecorder.Code)
	}

	if testRecorder.Body.String() != "hello world" {
		t.Errorf(`Expected "%s" response body but got "%s"`, "hello world", testRecorder.Body.String())
	}
}
<|endoftext|>


<commit_before>package model

import "testing"

func TestDocSyncKeys(t *testing.T) {
	d := Document{}

	// Add initial key to test
	d.SyncKeys([]string{"testkey1", "testkey2"}, true)
	if _, ok := d.Pairs["testkey1"]; !ok {
		t.Fatal("expected 'testkey1' to be present")
	}
	if _, ok := d.Pairs["testkey2"]; !ok {
		t.Fatal("expected 'testkey2' to be present")
	}

	// Test sync keys non-additive
	d.SyncKeys([]string{"testkey1"}, false)
	if _, ok := d.Pairs["testkey2"]; ok {
		t.Fatal("expected 'testkey2' to not be present")
	}
}

func TestDocSyncKeysAdditive(t *testing.T) {
	d := Document{}

	// Add initial key to test
	d.SyncKeys([]string{"testkey1"}, false)
	if _, ok := d.Pairs["testkey1"]; !ok {
		t.Fatal("expected 'testkey1' to be present")
	}

	// Set intial key value
	d.Pairs["testkey1"] = "testvalue1"

	// Test sync keys additive
	d.SyncKeys([]string{"testkey1", "testkey2"}, true)
	if v, _ := d.Pairs["testkey1"]; v != "testvalue1" {
		t.Fatalf("expected 'testkey1' value to be 'testvalue1', got: '%s'", v)
	}
	if _, ok := d.Pairs["testkey2"]; !ok {
		t.Fatal("expected 'testkey2' to not be present")
	}
}
<commit_msg>Fix test fail message<commit_after>package model

import "testing"

func TestDocSyncKeys(t *testing.T) {
	d := Document{}

	// Add initial key to test
	d.SyncKeys([]string{"testkey1", "testkey2"}, true)
	if _, ok := d.Pairs["testkey1"]; !ok {
		t.Fatal("expected 'testkey1' to be present")
	}
	if _, ok := d.Pairs["testkey2"]; !ok {
		t.Fatal("expected 'testkey2' to be present")
	}

	// Test sync keys non-additive
	d.SyncKeys([]string{"testkey1"}, false)
	if _, ok := d.Pairs["testkey2"]; ok {
		t.Fatal("expected 'testkey2' to not be present")
	}
}

func TestDocSyncKeysAdditive(t *testing.T) {
	d := Document{}

	// Add initial key to test
	d.SyncKeys([]string{"testkey1"}, true)
	if _, ok := d.Pairs["testkey1"]; !ok {
		t.Fatal("expected 'testkey1' to be present")
	}

	// Set intial key value
	d.Pairs["testkey1"] = "testvalue1"

	// Test sync keys additive
	d.SyncKeys([]string{"testkey1", "testkey2"}, true)
	if v, _ := d.Pairs["testkey1"]; v != "testvalue1" {
		t.Fatalf("expected 'testkey1' value to be 'testvalue1', got: '%s'", v)
	}
	if _, ok := d.Pairs["testkey2"]; !ok {
		t.Fatal("expected 'testkey2' to be present")
	}
}
<|endoftext|>


<commit_before>package mongoproto

import "io"

// OpGetMore is used to query the database for documents in a collection.
// http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-get-more
type OpGetMore struct {
	Header             MsgHeader
	FullCollectionName string // "dbname.collectionname"
	NumberToReturn     int32  // number of documents to return
	CursorID           int64  // cursorID from the OpReply
}

func (op *OpGetMore) OpCode() OpCode {
	return OpCodeGetMore
}

func (op *OpGetMore) FromReader(r io.Reader) error {
	var b [8]byte
	if _, err := io.ReadFull(r, b[:4]); err != nil {
		return err
	}
	name, err := readCStringFromReader(r)
	if err != nil {
		return err
	}
	op.FullCollectionName = string(name)
	op.NumberToReturn = getInt32(b[:], 0)
	op.CursorID = getInt64(b[:], 4)
	if _, err := io.ReadFull(r, b[:]); err != nil {
		return err
	}
	return nil
}

func (op *OpGetMore) fromWire(b []byte) {
	b = b[4:] // skip ZERO
	op.FullCollectionName = readCString(b)
	b = b[len(op.FullCollectionName)+1:]
	op.NumberToReturn = getInt32(b, 0)
	op.CursorID = getInt64(b, 4)
}

func (op *OpGetMore) toWire() []byte {
	return nil
}
<commit_msg>Fix getmore parsing.<commit_after>package mongoproto

import "io"

// OpGetMore is used to query the database for documents in a collection.
// http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-get-more
type OpGetMore struct {
	Header             MsgHeader
	FullCollectionName string // "dbname.collectionname"
	NumberToReturn     int32  // number of documents to return
	CursorID           int64  // cursorID from the OpReply
}

func (op *OpGetMore) OpCode() OpCode {
	return OpCodeGetMore
}

func (op *OpGetMore) FromReader(r io.Reader) error {
	var b [12]byte
	if _, err := io.ReadFull(r, b[:4]); err != nil {
		return err
	}
	name, err := readCStringFromReader(r)
	if err != nil {
		return err
	}
	op.FullCollectionName = string(name)
	if _, err := io.ReadFull(r, b[:12]); err != nil {
		return err
	}
	op.NumberToReturn = getInt32(b[:], 0)
	op.CursorID = getInt64(b[:], 4)
	if _, err := io.ReadFull(r, b[:]); err != nil {
		return err
	}
	return nil
}

func (op *OpGetMore) fromWire(b []byte) {
	b = b[4:] // skip ZERO
	op.FullCollectionName = readCString(b)
	b = b[len(op.FullCollectionName)+1:]
	op.NumberToReturn = getInt32(b, 0)
	op.CursorID = getInt64(b, 4)
}

func (op *OpGetMore) toWire() []byte {
	return nil
}
<|endoftext|>


<commit_before>package dwn

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

	"log"

	"github.com/satori/go.uuid"
	"golang.org/x/crypto/bcrypt"
)

func TokenHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	email := r.FormValue("email")
	plainPassword := r.FormValue("password")
	var user User
	err := Db.One("Email", email, &user)
	if err != nil {
		log.Println("could not load user to build session:", err)
		http.Error(w, "incorrect email or password", http.StatusUnauthorized)
		return
	}
	if CheckPasswordHash(plainPassword, user.Password) {
		session := Session{
			Token:     uuid.NewV4(),
			User:      user,
			CreatedAt: time.Now(),
			HeartBeat: time.Now(),
		}
		err = Db.Save(&session)
		if err != nil {
			log.Println("could not save session:", err)
			http.Error(w, "could not save session", http.StatusInternalServerError)
			return
		}
		json.NewEncoder(w).Encode(session)
	} else {
		log.Println("incorrect password:", plainPassword, "for user:", user)
		http.Error(w, "incorrect email or password", http.StatusUnauthorized)
		return
	}
}

const (
	_         = iota
	RoleAdmin = iota
	RoleUser  = iota
)

type User struct {
	ID        int    `storm:"id,increment"`
	Role      int    `storm:"index"`
	Email     string `storm:"unique"`
	Password  string
	Name      string
	CreatedAt time.Time
}

type Session struct {
	Token     uuid.UUID `storm:"id"`
	User      User      `storm:"index"`
	CreatedAt time.Time
	HeartBeat time.Time
}

func HashPassword(password string) (string, error) {
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
	return string(bytes), err
}

func CheckPasswordHash(password, hash string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
	return err == nil
}
<commit_msg>add more error handling<commit_after>package dwn

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

	"log"

	"github.com/satori/go.uuid"
	"golang.org/x/crypto/bcrypt"
)

func TokenHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	email := r.FormValue("email")
	plainPassword := r.FormValue("password")
	var user User
	err := Db.One("Email", email, &user)
	if err != nil {
		log.Println("could not load user to build session:", err, "for email", email)
		http.Error(w, "incorrect email or password", http.StatusUnauthorized)
		return
	}
	if CheckPasswordHash(plainPassword, user.Password) {
		session := Session{
			Token:     uuid.NewV4(),
			User:      user,
			CreatedAt: time.Now(),
			HeartBeat: time.Now(),
		}
		err = Db.Save(&session)
		if err != nil {
			log.Println("could not save session:", err)
			http.Error(w, "could not save session", http.StatusInternalServerError)
			return
		}
		json.NewEncoder(w).Encode(session)
	} else {
		log.Println("incorrect password:", plainPassword, "for user:", user)
		http.Error(w, "incorrect email or password", http.StatusUnauthorized)
		return
	}
}

const (
	_         = iota
	RoleAdmin = iota
	RoleUser  = iota
)

type User struct {
	ID        int    `storm:"id,increment"`
	Role      int    `storm:"index"`
	Email     string `storm:"unique"`
	Password  string
	Name      string
	CreatedAt time.Time
}

type Session struct {
	Token     uuid.UUID `storm:"id"`
	User      User      `storm:"index"`
	CreatedAt time.Time
	HeartBeat time.Time
}

func HashPassword(password string) (string, error) {
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
	return string(bytes), err
}

func CheckPasswordHash(password, hash string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
	return err == nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Igor Dolzhikov. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

/*
Package ecbrates 0.1.3

Example:

	package main

	import (
		"fmt"
		"log"

		"github.com/takama/ecbrates"
	)

	func main() {
		r, err := ecbrates.New()
		if err != nil {
			log.Fatal("Error: ", err)
		}

		// Case 1: get dollar rate relative to euro
		fmt.Println("Exchange rate", r.Date, ": EUR 1 -> USD", r.Rate[ecbrates.USD])

		// Case 2: convert of 100 euros to dollars
		if value, err := r.Convert(100, ecbrates.EUR, ecbrates.USD); err == nil {
			fmt.Println("Exchange rate", r.Date, ": EUR 100.0 -> USD", value)
		}
	}

European Central Bank exchange rates
*/
package ecbrates

import (
	"encoding/xml"
	"errors"
	"math"
	"net/http"
)

// Links to all supported currencies
const (
	EUR Currency = "EUR"
	USD Currency = "USD"
	JPY Currency = "JPY"
	BGN Currency = "BGN"
	CZK Currency = "CZK"
	DKK Currency = "DKK"
	GBP Currency = "GBP"
	HUF Currency = "HUF"
	LTL Currency = "LTL"
	PLN Currency = "PLN"
	RON Currency = "RON"
	SEK Currency = "SEK"
	CHF Currency = "CHF"
	NOK Currency = "NOK"
	HRK Currency = "HRK"
	RUB Currency = "RUB"
	TRY Currency = "TRY"
	AUD Currency = "AUD"
	BRL Currency = "BRL"
	CAD Currency = "CAD"
	CNY Currency = "CNY"
	HKD Currency = "HKD"
	IDR Currency = "IDR"
	ILS Currency = "ILS"
	INR Currency = "INR"
	KRW Currency = "KRW"
	MXN Currency = "MXN"
	MYR Currency = "MYR"
	NZD Currency = "NZD"
	PHP Currency = "PHP"
	SGD Currency = "SGD"
	THB Currency = "THB"
	ZAR Currency = "ZAR"

	ratesURL = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"
)

// Currency type as a link to string
type Currency string

// Rates represent date and currency exchange rates
type Rates struct {
	Date string
	Rate map[Currency]float32
}

// New - create a new instance of the rates and fetch a data from ECB
func New() (*Rates, error) {
	r := new(Rates)
	err := r.fetch()
	return r, err
}

// Convert a value "from" one Currency -> "to" other Currency
func (r *Rates) Convert(value float32, from, to Currency) (float32, error) {
	if r.Rate[to] == 0 || r.Rate[from] == 0 {
		return 0, errors.New("Perhaps one of the values of currencies is zero")
	}
	return round32(value*r.Rate[to]/r.Rate[from], 4), nil
}

// ECB XML envelope
type envelope struct {
	Data struct {
		Date  string `xml:"time,attr"`
		Rates []struct {
			Currency string  `xml:"currency,attr"`
			Rate     float32 `xml:"rate,attr"`
		} `xml:"Cube"`
	} `xml:"Cube>Cube"`
}

// Fetch an exchange rates
func (r *Rates) fetch() error {
	r.Rate = make(map[Currency]float32)

	// an exchange rates fetched relatively the EUR currency
	r.Rate[EUR] = 1

	response, err := http.Get(ratesURL)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	var raw envelope

	if err := xml.NewDecoder(response.Body).Decode(&raw); err != nil {
		return err
	}

	r.Date = raw.Data.Date

	for _, item := range raw.Data.Rates {
		r.Rate[Currency(item.Currency)] = item.Rate
	}

	return nil
}

func round32(x float32, prec int) float32 {
	if math.IsNaN(float64(x)) || math.IsInf(float64(x), 0) {
		return x
	}

	sign := 1.0
	if x < 0 {
		sign = -1
		x *= -1
	}

	var rounder float64
	pow := math.Pow(10, float64(prec))
	intermed := float64(x) * pow
	_, frac := math.Modf(intermed)

	if frac >= 0.5 {
		rounder = math.Ceil(intermed)
	} else {
		rounder = math.Floor(intermed)
	}

	return float32(rounder / pow * sign)
}
<commit_msg>Bumped version number to 0.1.4<commit_after>// Copyright 2014 Igor Dolzhikov. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

/*
Package ecbrates 0.1.4

Example:

	package main

	import (
		"fmt"
		"log"

		"github.com/takama/ecbrates"
	)

	func main() {
		r, err := ecbrates.New()
		if err != nil {
			log.Fatal("Error: ", err)
		}

		// Case 1: get dollar rate relative to euro
		fmt.Println("Exchange rate", r.Date, ": EUR 1 -> USD", r.Rate[ecbrates.USD])

		// Case 2: convert of 100 euros to dollars
		if value, err := r.Convert(100, ecbrates.EUR, ecbrates.USD); err == nil {
			fmt.Println("Exchange rate", r.Date, ": EUR 100.0 -> USD", value)
		}
	}

European Central Bank exchange rates
*/
package ecbrates

import (
	"encoding/xml"
	"errors"
	"math"
	"net/http"
)

// Links to all supported currencies
const (
	EUR Currency = "EUR"
	USD Currency = "USD"
	JPY Currency = "JPY"
	BGN Currency = "BGN"
	CZK Currency = "CZK"
	DKK Currency = "DKK"
	GBP Currency = "GBP"
	HUF Currency = "HUF"
	LTL Currency = "LTL"
	PLN Currency = "PLN"
	RON Currency = "RON"
	SEK Currency = "SEK"
	CHF Currency = "CHF"
	NOK Currency = "NOK"
	HRK Currency = "HRK"
	RUB Currency = "RUB"
	TRY Currency = "TRY"
	AUD Currency = "AUD"
	BRL Currency = "BRL"
	CAD Currency = "CAD"
	CNY Currency = "CNY"
	HKD Currency = "HKD"
	IDR Currency = "IDR"
	ILS Currency = "ILS"
	INR Currency = "INR"
	KRW Currency = "KRW"
	MXN Currency = "MXN"
	MYR Currency = "MYR"
	NZD Currency = "NZD"
	PHP Currency = "PHP"
	SGD Currency = "SGD"
	THB Currency = "THB"
	ZAR Currency = "ZAR"

	ratesURL = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"
)

// Currency type as a link to string
type Currency string

// Rates represent date and currency exchange rates
type Rates struct {
	Date string
	Rate map[Currency]float32
}

// New - create a new instance of the rates and fetch a data from ECB
func New() (*Rates, error) {
	r := new(Rates)
	err := r.fetch()
	return r, err
}

// Convert a value "from" one Currency -> "to" other Currency
func (r *Rates) Convert(value float32, from, to Currency) (float32, error) {
	if r.Rate[to] == 0 || r.Rate[from] == 0 {
		return 0, errors.New("Perhaps one of the values of currencies is zero")
	}
	return round32(value*r.Rate[to]/r.Rate[from], 4), nil
}

// ECB XML envelope
type envelope struct {
	Data struct {
		Date  string `xml:"time,attr"`
		Rates []struct {
			Currency string  `xml:"currency,attr"`
			Rate     float32 `xml:"rate,attr"`
		} `xml:"Cube"`
	} `xml:"Cube>Cube"`
}

// Fetch an exchange rates
func (r *Rates) fetch() error {
	r.Rate = make(map[Currency]float32)

	// an exchange rates fetched relatively the EUR currency
	r.Rate[EUR] = 1

	response, err := http.Get(ratesURL)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	var raw envelope

	if err := xml.NewDecoder(response.Body).Decode(&raw); err != nil {
		return err
	}

	r.Date = raw.Data.Date

	for _, item := range raw.Data.Rates {
		r.Rate[Currency(item.Currency)] = item.Rate
	}

	return nil
}

func round32(x float32, prec int) float32 {
	if math.IsNaN(float64(x)) || math.IsInf(float64(x), 0) {
		return x
	}

	sign := 1.0
	if x < 0 {
		sign = -1
		x *= -1
	}

	var rounder float64
	pow := math.Pow(10, float64(prec))
	intermed := float64(x) * pow
	_, frac := math.Modf(intermed)

	if frac >= 0.5 {
		rounder = math.Ceil(intermed)
	} else {
		rounder = math.Floor(intermed)
	}

	return float32(rounder / pow * sign)
}
<|endoftext|>


<commit_before>package gtfsconv

import (
  "fmt"
  "database/sql"
  "github.com/mattn/go-sqlite3"
)

// buildSpatialite enables Spatialite SQLite extension,
// and creates additional spatial-enhanced tables.
func buildSpatialite(name string) (*sql.DB, error) {

  // ensure sqlite db exists
  if isExistFile(name) == false {
    return nil, fmt.Errorf("failed to find %s sqlite db", name)
  }

  // register sqlite driver, w/ spatialite ext
  sql.Register("spatialite",
    &sqlite3.SQLiteDriver{
      Extensions: []string{

        // note: needs to exist on system
        "libspatialite",
      },
    })

  // open new db connection
  db, dbErr := sql.Open("spatialite", name)
  if dbErr != nil {
    return nil, fmt.Errorf(
      "failed to open existing sqlite db with spatialite [%s]", dbErr)
  }
  if hasDBSpatialite(db) == false {
    return nil, fmt.Errorf("spatialite not loaded")
  }

  // check for spatialite metadata tables
  if hasDBTable(db, "geometry_columns") == false ||
     hasDBTable(db, "spatial_ref_sys") == false {

     // drop existing tables (for safety)
     _, dgcErr := db.Exec("drop table if exists geometry_columns;")
     _, dsrsErr := db.Exec("drop table if exists spatial_ref_sys;")
    if dgcErr != nil || dsrsErr != nil {
       return nil, fmt.Errorf(
         "failed to drop prev spatial metadata tables [%s, %s]",
         dgcErr, dsrsErr)
     }

     // initialize spatialite metadata
     if _, initErr := db.Exec("select InitSpatialMetaData();");
       initErr != nil {
       return nil, fmt.Errorf(
         "failed to call `InitSpatialMetaData()` [%s]", initErr)
     }
  }

  if stopsErr := buildSpatialStops(db); stopsErr != nil {
    return nil, fmt.Errorf("buildSpatialStops() %s", stopsErr)
  }

  if hasDBTable(db, "shapes") { // only build, if "shapes" table exists
    if shapesErr := buildSpatialShapes(db); shapesErr != nil {
      return nil, fmt.Errorf("buildSpatialShapes() %s", shapesErr)
    }
  }

  return db, nil
}

// buildSpatialShapes Helper: Build "shapes_geo" spatialite table.
// note: "shapes" table must exist in db!
func buildSpatialShapes(db *sql.DB) error {

  // count current number of shapes, for sanity checking,
  numShapes, nsErr := countDBTable(db, "distinct(shape_id)", "shapes")
  if nsErr != nil {
    return fmt.Errorf("countDBTable() %s", nsErr)
  }

  // if "shapes_geo" already exists
  if hasDBTable(db, "shapes_geo") {

    // count current number of shapes_geo
    numGeo, ngErr := countDBTable(db, "*", "shapes_geo")
    if ngErr != nil {
      return fmt.Errorf("countDBTable() %s", ngErr)
    }

    // if complete table, do nothing
    if numGeo == numShapes {
      return nil
    }

    // otherwise, drop for rebuilding
    if _, dgErr := db.Exec("drop table shapes_geo;"); dgErr != nil {
      return fmt.Errorf("failed to drop prev shapes_geo table [%s]", dgErr)
    }
  }

  // create new "shapes_geo" table
  if _, cErr := db.Exec("create table shapes_geo (shape_id text);");
    cErr != nil {
    return fmt.Errorf("failed to create table `shapes_geo` [%s]", cErr)
  }

  // add spatialite geometry column
  if _, gErr := db.Exec("select AddGeometryColumn" +
                      "('shapes_geo', 'geom', 4326, 'LINESTRING');");
    gErr != nil {
    return fmt.Errorf("failed to add column `shapes_geo.geom` [%s]", gErr)
  }

  // process each existing "shapes.shape_id" into "shapes_geo"
  if _, iErr := db.Exec("insert into shapes_geo " +
                     "select shape_id, geomfromtext(" +
                       "'LINESTRING(' || " +
                         "group_concat(shape_pt_lon || ' ' || shape_pt_lat) " +
                       " || ')', " +
                     "4326) as geom " +
                     "from shapes group by shape_id;");
    iErr != nil {
    return fmt.Errorf("failed to insert rows into `shapes_geo` [%s]", iErr)
  }

  // count "shapes_geo" for final sanity check
  numGeo, ngErr := countDBTable(db, "*", "shapes_geo")
  if ngErr != nil {
    return fmt.Errorf("countDBTable() %s", ngErr)
  }

  // confirm proper number of rows
  if numGeo != numShapes {
    return fmt.Errorf(
      "failed to sanity check shapes_geo rows: %v expected, %v actual",
      numShapes, numGeo)
  }

  return nil
}

// buildSpatialStops Helper: Build "stops_geo" spatialite table.
func buildSpatialStops(db *sql.DB) error {

  // count current number of stops, for sanity checking,
  numStops, nsErr := countDBTable(db, "*", "stops")
  if nsErr != nil {
    return fmt.Errorf("countDBTable() %s", nsErr)
  }

  // if "stops_geo" already exists
  if hasDBTable(db, "stops_geo") {

    // count current number of shapes_geo
    numGeo, ngErr := countDBTable(db, "*", "stops_geo")
    if ngErr != nil {
      return fmt.Errorf("countDBTable() %s", ngErr)
    }

    // if complete table, do nothing
    if numGeo == numStops {
      return nil
    }

    // otherwise, drop for rebuilding
    if _, dgErr := db.Exec("drop table stops_geo;"); dgErr != nil {
      return fmt.Errorf("failed to drop prev stops_geo table [%s]", dgErr)
    }
  }

  // create new "stops_geo" table
  if _, cErr := db.Exec("create table stops_geo (stop_id text);");
    cErr != nil {
    return fmt.Errorf("failed to create table `stops_geo` [%s]", cErr)
  }

  // add spatialite geometry column
  if _, gErr := db.Exec("select AddGeometryColumn" +
                      "('stops_geo', 'geom', 4326, 'POINT');");
    gErr != nil {
    return fmt.Errorf("failed to add column `stops_geo.geom` [%s]", gErr)
  }

  // process each existing "stops.stop_id" into "stops_geo"
  if _, iErr := db.Exec("insert into stops_geo (stop_id, geom) " +
                      "select stop_id, geomfromtext(" +
                        "'POINT('||stop_lat||' '||stop_lon||')'" +
                      ", 4326) from stops;");
    iErr != nil {
    return fmt.Errorf("failed to insert rows into `stops_geo` [%s]", iErr)
  }

  // count "stops_geo" for final sanity check
  numGeo, ngErr := countDBTable(db, "*", "stops_geo")
  if ngErr != nil {
    return fmt.Errorf("countDBTable() %s", ngErr)
  }

  // confirm proper number of rows
  if numGeo != numStops {
    return fmt.Errorf(
      "failed to sanity check stops_geo rows: %v expected, %v actual",
      numStops, numGeo)
  }

  return nil
}
<commit_msg>... it's the little things in life. </3<commit_after>package gtfsconv

import (
  "fmt"
  "database/sql"
  "github.com/mattn/go-sqlite3"
)

// buildSpatialite enables Spatialite SQLite extension,
// and creates additional spatial-enhanced tables.
func buildSpatialite(name string) (*sql.DB, error) {

  // ensure sqlite db exists
  if isExistFile(name) == false {
    return nil, fmt.Errorf("failed to find %s sqlite db", name)
  }

  // register sqlite driver, w/ spatialite ext
  sql.Register("spatialite",
    &sqlite3.SQLiteDriver{
      Extensions: []string{

        // note: needs to exist on system
        "libspatialite",
      },
    })

  // open new db connection
  db, dbErr := sql.Open("spatialite", name)
  if dbErr != nil {
    return nil, fmt.Errorf(
      "failed to open existing sqlite db with spatialite [%s]", dbErr)
  }
  if hasDBSpatialite(db) == false {
    return nil, fmt.Errorf("spatialite not loaded")
  }

  // check for spatialite metadata tables
  if hasDBTable(db, "geometry_columns") == false ||
     hasDBTable(db, "spatial_ref_sys") == false {

     // drop existing tables (for safety)
     _, dgcErr := db.Exec("drop table if exists geometry_columns;")
     _, dsrsErr := db.Exec("drop table if exists spatial_ref_sys;")
    if dgcErr != nil || dsrsErr != nil {
       return nil, fmt.Errorf(
         "failed to drop prev spatial metadata tables [%s, %s]",
         dgcErr, dsrsErr)
     }

     // initialize spatialite metadata
     if _, initErr := db.Exec("select InitSpatialMetaData();");
       initErr != nil {
       return nil, fmt.Errorf(
         "failed to call `InitSpatialMetaData()` [%s]", initErr)
     }
  }

  if stopsErr := buildSpatialStops(db); stopsErr != nil {
    return nil, fmt.Errorf("buildSpatialStops() %s", stopsErr)
  }

  if hasDBTable(db, "shapes") { // only build, if "shapes" table exists
    if shapesErr := buildSpatialShapes(db); shapesErr != nil {
      return nil, fmt.Errorf("buildSpatialShapes() %s", shapesErr)
    }
  }

  return db, nil
}

// buildSpatialShapes Helper: Build "shapes_geo" spatialite table.
// note: "shapes" table must exist in db!
func buildSpatialShapes(db *sql.DB) error {

  // count current number of shapes, for sanity checking,
  numShapes, nsErr := countDBTable(db, "distinct(shape_id)", "shapes")
  if nsErr != nil {
    return fmt.Errorf("countDBTable() %s", nsErr)
  }

  // if "shapes_geo" already exists
  if hasDBTable(db, "shapes_geo") {

    // count current number of shapes_geo
    numGeo, ngErr := countDBTable(db, "*", "shapes_geo")
    if ngErr != nil {
      return fmt.Errorf("countDBTable() %s", ngErr)
    }

    // if complete table, do nothing
    if numGeo == numShapes {
      return nil
    }

    // otherwise, drop for rebuilding
    if _, dgErr := db.Exec("drop table shapes_geo;"); dgErr != nil {
      return fmt.Errorf("failed to drop prev shapes_geo table [%s]", dgErr)
    }
  }

  // create new "shapes_geo" table
  if _, cErr := db.Exec("create table shapes_geo (shape_id text);");
    cErr != nil {
    return fmt.Errorf("failed to create table `shapes_geo` [%s]", cErr)
  }

  // add spatialite geometry column
  if _, gErr := db.Exec("select AddGeometryColumn" +
                      "('shapes_geo', 'geom', 4326, 'LINESTRING');");
    gErr != nil {
    return fmt.Errorf("failed to add column `shapes_geo.geom` [%s]", gErr)
  }

  // process each existing "shapes.shape_id" into "shapes_geo"
  if _, iErr := db.Exec("insert into shapes_geo " +
                     "select shape_id, geomfromtext(" +
                       "'LINESTRING(' || " +
                         "group_concat(shape_pt_lon || ' ' || shape_pt_lat) " +
                       " || ')', " +
                     "4326) as geom " +
                     "from shapes group by shape_id;");
    iErr != nil {
    return fmt.Errorf("failed to insert rows into `shapes_geo` [%s]", iErr)
  }

  // count "shapes_geo" for final sanity check
  numGeo, ngErr := countDBTable(db, "*", "shapes_geo")
  if ngErr != nil {
    return fmt.Errorf("countDBTable() %s", ngErr)
  }

  // confirm proper number of rows
  if numGeo != numShapes {
    return fmt.Errorf(
      "failed to sanity check shapes_geo rows: %v expected, %v actual",
      numShapes, numGeo)
  }

  return nil
}

// buildSpatialStops Helper: Build "stops_geo" spatialite table.
func buildSpatialStops(db *sql.DB) error {

  // count current number of stops, for sanity checking,
  numStops, nsErr := countDBTable(db, "*", "stops")
  if nsErr != nil {
    return fmt.Errorf("countDBTable() %s", nsErr)
  }

  // if "stops_geo" already exists
  if hasDBTable(db, "stops_geo") {

    // count current number of shapes_geo
    numGeo, ngErr := countDBTable(db, "*", "stops_geo")
    if ngErr != nil {
      return fmt.Errorf("countDBTable() %s", ngErr)
    }

    // if complete table, do nothing
    if numGeo == numStops {
      return nil
    }

    // otherwise, drop for rebuilding
    if _, dgErr := db.Exec("drop table stops_geo;"); dgErr != nil {
      return fmt.Errorf("failed to drop prev stops_geo table [%s]", dgErr)
    }
  }

  // create new "stops_geo" table
  if _, cErr := db.Exec("create table stops_geo (stop_id text);");
    cErr != nil {
    return fmt.Errorf("failed to create table `stops_geo` [%s]", cErr)
  }

  // add spatialite geometry column
  if _, gErr := db.Exec("select AddGeometryColumn" +
                      "('stops_geo', 'geom', 4326, 'POINT');");
    gErr != nil {
    return fmt.Errorf("failed to add column `stops_geo.geom` [%s]", gErr)
  }

  // process each existing "stops.stop_id" into "stops_geo"
  if _, iErr := db.Exec("insert into stops_geo (stop_id, geom) " +
                      "select stop_id, geomfromtext(" +
                        "'POINT('||stop_lon||' '||stop_lat||')'" +
                      ", 4326) from stops;");
    iErr != nil {
    return fmt.Errorf("failed to insert rows into `stops_geo` [%s]", iErr)
  }

  // count "stops_geo" for final sanity check
  numGeo, ngErr := countDBTable(db, "*", "stops_geo")
  if ngErr != nil {
    return fmt.Errorf("countDBTable() %s", ngErr)
  }

  // confirm proper number of rows
  if numGeo != numStops {
    return fmt.Errorf(
      "failed to sanity check stops_geo rows: %v expected, %v actual",
      numStops, numGeo)
  }

  return nil
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"github.com/stretchr/testify/assert"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"
	"testing"

	"github.com/eirka/eirka-libs/db"
	e "github.com/eirka/eirka-libs/errors"
)

func TestEmailIsValid(t *testing.T) {

	email := EmailModel{
		Uid:   0,
		Name:  "test",
		Email: "cool@test.com",
	}

	assert.False(t, email.IsValid(), "Should be false")

}

func TestEmailValidate(t *testing.T) {

	var err error

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	rows := sqlmock.NewRows([]string{"name", "email"}).AddRow("test", "old@test.com")
	mock.ExpectQuery(`SELECT user_name,user_email FROM users WHERE user_id`).WillReturnRows(rows)

	email := EmailModel{
		Uid:   1,
		Email: "cool@test.com",
	}

	err = email.Validate()
	if assert.NoError(t, err, "An error was not expected") {
		assert.Equal(t, email.Name, "test", "Should match")
		assert.Equal(t, email.CurrentEmail, "old@test.com", "Should match")
	}
}
<commit_msg>add model tests<commit_after>package models

import (
	//"errors"
	"github.com/stretchr/testify/assert"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"
	"testing"

	"github.com/eirka/eirka-libs/db"
	//e "github.com/eirka/eirka-libs/errors"
)

func TestEmailIsValid(t *testing.T) {

	email := EmailModel{
		Uid:   0,
		Name:  "test",
		Email: "cool@test.com",
	}

	assert.False(t, email.IsValid(), "Should be false")

}

func TestEmailValidate(t *testing.T) {

	var err error

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	rows := sqlmock.NewRows([]string{"name", "email"}).AddRow("test", "old@test.com")
	mock.ExpectQuery(`SELECT user_name,user_email FROM users WHERE user_id`).WillReturnRows(rows)

	email := EmailModel{
		Uid:   1,
		Email: "cool@test.com",
	}

	err = email.Validate()
	if assert.NoError(t, err, "An error was not expected") {
		assert.Equal(t, email.Name, "test", "Should match")
		assert.Equal(t, email.CurrentEmail, "old@test.com", "Should match")
	}
}
<|endoftext|>


<commit_before><commit_msg>ethereum instead of ethereal. Fixes #69<commit_after><|endoftext|>


<commit_before>package models

import ()

type Simulation struct {
	Name     string
	Contents string `datastore:",noindex"`
	UserID   string
	// Tell datastore to ignore thie field
	Id int64 `datastore:"-"`
}
<commit_msg>Adding comments to simulation model<commit_after>package models

import ()

type Simulation struct {
	Name     string
  // Tell datastore not to index this field, increase max size
  // from 1500 bytes to ~ 1 MB
	Contents string `datastore:",noindex"`
	UserID   string
	// Tell datastore to ignore thie field
	Id int64 `datastore:"-"`
}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2014 William Miller

// 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.

package fedops

import (
  "fmt"
  "crypto/tls"
  "crypto/x509"
  //
)

func (d *Dispatcher) OpenConnection(vmID string) *tls.Conn {
  // server cert is self signed -> server_cert == ca_cert
  certPool := x509.NewCertPool()

  fed_certs := d.Config.Certs
  certPool.AppendCertsFromPEM(fed_certs[0].CertificatePem)

  config := tls.Config{RootCAs: certPool}

  ip := "104.236.224.246"
  conn, err := tls.Dial("tcp", ip + ":13371", &config)
  if err != nil {
    fmt.Println("client: dial:", err.Error())
    return nil
  }
  // defer conn.Close()
  return conn
}
<commit_msg>ugh, tls certs<commit_after>// The MIT License (MIT)

// Copyright (c) 2014 William Miller

// 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.

package fedops

import (
  "fmt"
  "crypto/tls"
  "crypto/x509"
  //
)

func (d *Dispatcher) OpenConnection(vmID string) *tls.Conn {
  // server cert is self signed -> server_cert == ca_cert
  certPool := x509.NewCertPool()

  fed_certs := d.Config.Certs
  certPool.AppendCertsFromPEM(fed_certs[0].CertificatePem)

  config := tls.Config{RootCAs: certPool}

  ip := "127.0.0.1"
  conn, err := tls.Dial("tcp", ip + ":13371", &config)
  if err != nil {
    fmt.Println("client: dial:", err.Error())
    return nil
  }
  // defer conn.Close()
  return conn
}
<|endoftext|>


<commit_before>package main

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

	"github.com/coreos/go-etcd/etcd"
)

var (
	EtcdEnvironment = map[string]string{}
	EtcdTag         = "latest"
)

func EnvironmentListener(in, out chan Message) {
	send := Messenger(TopicEnvironment, out)

	client := etcd.NewClient(strings.Split(config.EtcdHosts, ","))

	tagKey := config.ConfigPrefix + "tag"
	envKey := config.ConfigPrefix + "env"

	watch := make(chan *etcd.Response, 10)
	watchStop := make(chan bool)

	for {
		select {
		case message := <-in:
			switch message.Topic {
			case TopicInit:
				send(LevelInfo, fmt.Sprintf("setting watch on %s", config.ConfigPrefix))
				go client.Watch(config.ConfigPrefix, 0, true, watch, watchStop)

				send(LevelInfo, "getting initial configuration from etcd")
				resp, err := client.Get(tagKey, false, false)
				if err != nil {
					send(LevelFatal, fmt.Sprintf("failed to get tag: %s", err))
				}
				watch <- resp

				resp, err = client.Get(envKey, false, false)
				if err != nil {
					send(LevelFatal, fmt.Sprintf("failed to get env: %s", err))
				}
				watch <- resp

			case TopicShutdown:
				watchStop <- true
				send(LevelInfo, fmt.Sprintf("cleared watches on %s", config.ConfigPrefix))
			}

		case resp := <-watch:
			switch resp.Node.Key {
			case tagKey:
				EtcdTag = resp.Node.Value
				send(LevelChange, fmt.Sprintf("tag is %s", EtcdTag))

			case envKey:
				err := json.Unmarshal([]byte(resp.Node.Value), &EtcdEnvironment)
				if err != nil {
					send(LevelFatal, fmt.Sprintf("error loading env: %s", err))
				}
				send(LevelChange, fmt.Sprintf("environment is %s", EtcdEnvironment))

			default:
				send(LevelDebug, fmt.Sprintf("unknown config key: %s", resp.Node.Key))
			}
		}
	}
}
<commit_msg>environment: fix nil value<commit_after>package main

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

	"github.com/coreos/go-etcd/etcd"
)

var (
	EtcdEnvironment = map[string]string{}
	EtcdTag         = "latest"
)

func EnvironmentListener(in, out chan Message) {
	send := Messenger(TopicEnvironment, out)

	client := etcd.NewClient(strings.Split(config.EtcdHosts, ","))

	tagKey := config.ConfigPrefix + "tag"
	envKey := config.ConfigPrefix + "env"

	watch := make(chan *etcd.Response, 10)
	watchStop := make(chan bool)

	for {
		select {
		case message := <-in:
			switch message.Topic {
			case TopicInit:
				send(LevelInfo, fmt.Sprintf("setting watch on %s", config.ConfigPrefix))
				go client.Watch(config.ConfigPrefix, 0, true, watch, watchStop)

				send(LevelInfo, "getting initial configuration from etcd")
				resp, err := client.Get(tagKey, false, false)
				if err != nil {
					send(LevelFatal, fmt.Sprintf("failed to get tag: %s", err))
				}
				watch <- resp

				resp, err = client.Get(envKey, false, false)
				if err != nil {
					send(LevelFatal, fmt.Sprintf("failed to get env: %s", err))
				}
				watch <- resp

			case TopicShutdown:
				watchStop <- true
				send(LevelInfo, fmt.Sprintf("cleared watches on %s", config.ConfigPrefix))
				return
			}

		case resp := <-watch:
			if resp == nil {
				send(LevelWarning, "received a nil response")
				continue
			}

			switch resp.Node.Key {
			case tagKey:
				EtcdTag = resp.Node.Value
				send(LevelChange, fmt.Sprintf("tag is %s", EtcdTag))

			case envKey:
				err := json.Unmarshal([]byte(resp.Node.Value), &EtcdEnvironment)
				if err != nil {
					send(LevelFatal, fmt.Sprintf("error loading env: %s", err))
				}
				send(LevelChange, fmt.Sprintf("environment is %s", EtcdEnvironment))

			default:
				send(LevelDebug, fmt.Sprintf("unknown config key: %s", resp.Node.Key))
			}
		}
	}
}
<|endoftext|>