<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"syscall"
)

const Version string = "0.0.1"

type Grcron struct {
	StateFile    string
	DefaultState string
	CurrentState string
}

func (gr Grcron) Validate() error {
	_, err := os.Stat(gr.StateFile)
	if err != nil {
		return err
	}
	if !(gr.DefaultState == "active" || gr.DefaultState == "passive") {
		return fmt.Errorf("The Value of DefaultState:%s is incorrect.", gr.DefaultState)
	}
	return nil
}

func (gr *Grcron) ParseState() error {
	if gr == nil {
		return fmt.Errorf("Don't run nil Pointer Receiver.")
	}
	f, err := os.Open(gr.StateFile)
	defer f.Close()
	if err != nil {
		return err
	}

	sc := bufio.NewScanner(f)
	if !sc.Scan() {
		return sc.Err()
	}
	st := sc.Text()
	switch st {
	case "active", "passive":
		gr.CurrentState = st
	default:
		fmt.Fprintf(os.Stderr, "corrupted state file('%s') (content='%s'), staying at gr.DefaultState('%s')\n", gr.StateFile, st, gr.DefaultState)
		gr.CurrentState = gr.DefaultState
	}
	return nil
}
func (gr Grcron) IsActive() (bool, error) {
	cmd := exec.Command("sh", "-c", "ps cax | grep -q keepalived")
	err := cmd.Run()
	var exitStatus int
	if e2, ok := err.(*exec.ExitError); ok {
		if s, ok := e2.Sys().(syscall.WaitStatus); ok {
			exitStatus = s.ExitStatus()
		} else {
			return false, fmt.Errorf("Unimplemented for system where exec.ExitError.Sys() is not syscall.WaitStatus.")
		}
	} else {
		exitStatus = 0
	}

	if gr.CurrentState == "active" && exitStatus == 0 {
		return true, nil
	} else {
		if gr.CurrentState == "active" {
			fmt.Fprintf(os.Stderr, "gr.CurrentState:active, but keepalived is probably down.\n")
		}
		return false, nil
	}
}

func main() {
	var (
		showVersion bool
		dryRun      bool
	)

	gr := &Grcron{}
	flag.StringVar(&gr.StateFile, "f", "/var/run/grcron/state", "grcron state file.")
	flag.StringVar(&gr.DefaultState, "s", "passive", "grcron default state.")
	flag.BoolVar(&showVersion, "version", false, "show version number.")
	flag.BoolVar(&showVersion, "v", false, "show version number.")
	flag.BoolVar(&dryRun, "dryrun", false, "dry-run.")
	flag.BoolVar(&dryRun, "n", false, "dry-run.")
	flag.Parse()
	args := flag.Args()

	if showVersion {
		fmt.Printf("grcron %s, %s built for %s/%s\n", Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
		return
	}

	if err := gr.Validate(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if err := gr.ParseState(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if len(args) < 1 {
		fmt.Fprintln(os.Stderr, "not enough arguments")
		os.Exit(1)
	}

	isa, err := gr.IsActive()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if dryRun {
		fmt.Printf("dry-run gr.CurrentState:%s, gr.IsActive:%v finished.\n", gr.CurrentState, isa)
		return
	}

	if !isa {
		return
	}

	// run !!
	binary, err := exec.LookPath(args[0])
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if err := syscall.Exec(binary, args, os.Environ()); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<commit_msg>keepalivedがいないならエラーにする<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"syscall"
)

const Version string = "0.0.1"

type Grcron struct {
	StateFile    string
	DefaultState string
	CurrentState string
}

func (gr Grcron) Validate() error {
	_, err := os.Stat(gr.StateFile)
	if err != nil {
		return err
	}
	if !(gr.DefaultState == "active" || gr.DefaultState == "passive") {
		return fmt.Errorf("The Value of DefaultState:%s is incorrect.", gr.DefaultState)
	}
	return nil
}

func (gr *Grcron) ParseState() error {
	if gr == nil {
		return fmt.Errorf("Don't run nil Pointer Receiver.")
	}
	f, err := os.Open(gr.StateFile)
	defer f.Close()
	if err != nil {
		return err
	}

	sc := bufio.NewScanner(f)
	if !sc.Scan() {
		return sc.Err()
	}
	st := sc.Text()
	switch st {
	case "active", "passive":
		gr.CurrentState = st
	default:
		fmt.Fprintf(os.Stderr, "corrupted state file('%s') (content='%s'), staying at gr.DefaultState('%s')\n", gr.StateFile, st, gr.DefaultState)
		gr.CurrentState = gr.DefaultState
	}
	return nil
}
func (gr Grcron) IsActive() (bool, error) {
	cmd := exec.Command("sh", "-c", "ps cax | grep -q keepalived")
	err := cmd.Run()

	// 異常終了はkeepalivedプロセスがいないとみなす
	if _, ok := err.(*exec.ExitError); ok {
		return false, fmt.Errorf("keepalived is probably down.")
	}

	return gr.CurrentState == "active", nil
}

func main() {
	var (
		showVersion bool
		dryRun      bool
	)

	gr := &Grcron{}
	flag.StringVar(&gr.StateFile, "f", "/var/run/grcron/state", "grcron state file.")
	flag.StringVar(&gr.DefaultState, "s", "passive", "grcron default state.")
	flag.BoolVar(&showVersion, "version", false, "show version number.")
	flag.BoolVar(&showVersion, "v", false, "show version number.")
	flag.BoolVar(&dryRun, "dryrun", false, "dry-run.")
	flag.BoolVar(&dryRun, "n", false, "dry-run.")
	flag.Parse()
	args := flag.Args()

	if showVersion {
		fmt.Printf("grcron %s, %s built for %s/%s\n", Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
		return
	}

	if err := gr.Validate(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if err := gr.ParseState(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if len(args) < 1 {
		fmt.Fprintln(os.Stderr, "not enough arguments")
		os.Exit(1)
	}

	isa, err := gr.IsActive()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if dryRun {
		fmt.Printf("dry-run gr.CurrentState:%s, gr.IsActive:%v finished.\n", gr.CurrentState, isa)
		return
	}

	if !isa {
		return
	}

	// run !!
	binary, err := exec.LookPath(args[0])
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if err := syscall.Exec(binary, args, os.Environ()); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015, Timothy Bogdala <tdb@animal-machine.com>
// See the LICENSE file for more details.

/*

Package groggy is a library that makes it easier to setup custom
logging channels.

To use:

1) Call Register() with a log name and an optional function to handle the log events.
2) Call Log() with this log name and the data objects to log

If no optional log handlers are supplied, the default handler writes the data
objects out to stdout using fmt.Print(). To do this, the objects should be
strings or implement the fmt.Stringer interface.

If Log() is called with a log name that is not registered, it will not be able
to call a handler, and an error will be returned.

Clients can call Deregister() to remove a log handler.

*/
package groggy

import (
	"fmt"
	"sync"
	"time"
)

// GroggyEvent defines a function handler for incoming logging events.
// It's setup with a variadic argument for extra flexibility in custom handlers.
type GroggyEvent func(logName string, data ...interface{}) error

var (
	// handlers is a global registry of event handlers
	handlers map[string]GroggyEvent

	// make a mutex for the default sync handler
	handlerMutex sync.Mutex
)

func init() {
	// make sure to initialize the global map
	handlers = make(map[string]GroggyEvent)
}

// Register adds a new log handler to the global registry and assigns it
// the handler function passed in. If handler is nil, then DefaultHandler is used.
// An existing log handler can be replaced using this function.
func Register(newLogName string, handler GroggyEvent) {
	// use DefaultHandler if a nil handler was supplied
	var h GroggyEvent = handler
	if h == nil {
		h = DefaultHandler
	}
	handlers[newLogName] = h
}

// Deregister removes the log handler from the global registry so that
// further calls to Log with the log name do not get handled.
func Deregister(logName string) {
	delete(handlers, logName)
}

// DefaultHandler writes out the information assuming data members are strings
// or anything that implements the GoStringer interface. This is not
// considered safe for concurrency.
func DefaultHandler(logName string, data ...interface{}) error {
	const layout = "15:04:05.000"
	now := time.Now()
	fmt.Printf("%s %s: ", now.Format(layout), logName)
	for _, ds := range data {
		switch v := ds.(type) {
		case string:
			fmt.Print(v)
		case fmt.Stringer:
			fmt.Print(v.String())
		default:
			fmt.Printf("<unknown log data type %v>", ds)
		}
	}
	fmt.Print("\n")
	return nil
}

// DefaultSyncHandler writes out the information assuming data members are strings
// or anything that implements the GoStringer interface. This is considered safe
// for concurrency.
func DefaultSyncHandler(logName string, data ...interface{}) error {
	handlerMutex.Lock()
	DefaultHandler(logName, data...)
	handlerMutex.Unlock()
	return nil
}

// Log sends the data to the handler specified by the logName. This is not
// considered safe for concurrency by default.
func Log(logName string, data ...interface{}) error {
	h, okay := handlers[logName]
	if okay == false {
		return fmt.Errorf("No log handler found for %s.", logName)
	}

	return h(logName, data...)
}

func Logsf(logName string, data ...interface{}) error {
	h, okay := handlers[logName]
	if okay == false {
		return fmt.Errorf("No log handler found for %s.", logName)
	}

	sprintStr, okay := data[0].(string)
	if okay == false {
		return fmt.Errorf("A format string was not passed as the second parameter.")
	}

	s := fmt.Sprintf(sprintStr, data[1:]...)
	return h(logName, s)
}
<commit_msg>forgot to document Logsf. shame on me!<commit_after>// Copyright 2015, Timothy Bogdala <tdb@animal-machine.com>
// See the LICENSE file for more details.

/*

Package groggy is a library that makes it easier to setup custom
logging channels.

To use:

1) Call Register() with a log name and an optional function to handle the log events.
2) Call Log() with this log name and the data objects to log

If no optional log handlers are supplied, the default handler writes the data
objects out to stdout using fmt.Print(). To do this, the objects should be
strings or implement the fmt.Stringer interface.

If Log() is called with a log name that is not registered, it will not be able
to call a handler, and an error will be returned.

Clients can call Deregister() to remove a log handler.

*/
package groggy

import (
	"fmt"
	"sync"
	"time"
)

// GroggyEvent defines a function handler for incoming logging events.
// It's setup with a variadic argument for extra flexibility in custom handlers.
type GroggyEvent func(logName string, data ...interface{}) error

var (
	// handlers is a global registry of event handlers
	handlers map[string]GroggyEvent

	// make a mutex for the default sync handler
	handlerMutex sync.Mutex
)

func init() {
	// make sure to initialize the global map
	handlers = make(map[string]GroggyEvent)
}

// Register adds a new log handler to the global registry and assigns it
// the handler function passed in. If handler is nil, then DefaultHandler is used.
// An existing log handler can be replaced using this function.
func Register(newLogName string, handler GroggyEvent) {
	// use DefaultHandler if a nil handler was supplied
	var h GroggyEvent = handler
	if h == nil {
		h = DefaultHandler
	}
	handlers[newLogName] = h
}

// Deregister removes the log handler from the global registry so that
// further calls to Log with the log name do not get handled.
func Deregister(logName string) {
	delete(handlers, logName)
}

// DefaultHandler writes out the information assuming data members are strings
// or anything that implements the GoStringer interface. This is not
// considered safe for concurrency.
func DefaultHandler(logName string, data ...interface{}) error {
	const layout = "15:04:05.000"
	now := time.Now()
	fmt.Printf("%s %s: ", now.Format(layout), logName)
	for _, ds := range data {
		switch v := ds.(type) {
		case string:
			fmt.Print(v)
		case fmt.Stringer:
			fmt.Print(v.String())
		default:
			fmt.Printf("<unknown log data type %v>", ds)
		}
	}
	fmt.Print("\n")
	return nil
}

// DefaultSyncHandler writes out the information assuming data members are strings
// or anything that implements the GoStringer interface. This is considered safe
// for concurrency.
func DefaultSyncHandler(logName string, data ...interface{}) error {
	handlerMutex.Lock()
	DefaultHandler(logName, data...)
	handlerMutex.Unlock()
	return nil
}

// Log sends the data to the handler specified by the logName. This is not
// considered safe for concurrency by default.
func Log(logName string, data ...interface{}) error {
	h, okay := handlers[logName]
	if okay == false {
		return fmt.Errorf("No log handler found for %s.", logName)
	}

	return h(logName, data...)
}

// Logsf uses the second parameter as the format string for fmt.Spritnf and
// then sends the rest of the parameters to it. The resulting string is then
// passed to the handler specified by logName.
func Logsf(logName string, data ...interface{}) error {
	h, okay := handlers[logName]
	if okay == false {
		return fmt.Errorf("No log handler found for %s.", logName)
	}

	sprintStr, okay := data[0].(string)
	if okay == false {
		return fmt.Errorf("A format string was not passed as the second parameter.")
	}

	s := fmt.Sprintf(sprintStr, data[1:]...)
	return h(logName, s)
}
<|endoftext|>


<commit_before>package swarm

import (
	"net"
	"sync"
	"time"
	"xd/lib/bittorrent"
	"xd/lib/bittorrent/extensions"
	"xd/lib/common"
	"xd/lib/log"
	"xd/lib/util"
)

const DefaultMaxParallelRequests = 8

// a peer connection
type PeerConn struct {
	inbound             bool
	closing             bool
	c                   net.Conn
	id                  common.PeerID
	t                   *Torrent
	sendMtx             sync.Mutex
	bf                  *bittorrent.Bitfield
	peerChoke           bool
	peerInterested      bool
	usChoke             bool
	usInterested        bool
	Done                func()
	keepalive           *time.Ticker
	lastSend            time.Time
	tx                  util.Rate
	lastRecv            time.Time
	rx                  util.Rate
	downloading         []*common.PieceRequest
	ourOpts             *extensions.Message
	theirOpts           *extensions.Message
	MaxParalellRequests int
	access              sync.Mutex
}

// get stats for this connection
func (c *PeerConn) Stats() (st *PeerConnStats) {
	st = new(PeerConnStats)
	st.TX = c.tx.Rate()
	st.RX = c.rx.Rate()
	st.Addr = c.c.RemoteAddr().String()
	st.ID = c.id.String()
	return
}

func makePeerConn(c net.Conn, t *Torrent, id common.PeerID, ourOpts *extensions.Message) *PeerConn {
	p := new(PeerConn)
	p.c = c
	p.t = t
	p.ourOpts = ourOpts
	p.peerChoke = true
	p.usChoke = true
	copy(p.id[:], id[:])
	p.MaxParalellRequests = t.MaxRequests
	p.keepalive = time.NewTicker(time.Minute)
	p.downloading = []*common.PieceRequest{}
	return p
}

func (c *PeerConn) start() {
	go c.runReader()
	go c.runKeepAlive()
	go c.tickStats()
}

func (c *PeerConn) runKeepAlive() {
	for !c.closing {
		time.Sleep(time.Second)
		c.sendKeepAlive()
	}
}

func (c *PeerConn) tickStats() {
	for !c.closing {
		time.Sleep(time.Second)
		c.tx.Tick()
		c.rx.Tick()
	}
}

func (c *PeerConn) doSend(msg *common.WireMessage) {
	if !c.closing && msg != nil {
		c.sendMtx.Lock()
		now := time.Now()
		c.lastSend = now
		if c.RemoteChoking() && msg.MessageID() == common.Request {
			// drop
			log.Debugf("drop request because choke")
			r := msg.GetPieceRequest()
			c.cancelDownload(r)
		} else {
			log.Debugf("writing %d bytes", msg.Len())
			err := msg.Send(c.c)
			if err == nil {
				if msg.MessageID() == common.Piece {
					c.tx.AddSample(uint64(msg.Len()))
				}
				log.Debugf("wrote message %s %d bytes", msg.MessageID(), msg.Len())
			} else {
				log.Debugf("write error: %s", err.Error())
			}
		}
		c.sendMtx.Unlock()
	}
}

// queue a send of a bittorrent wire message to this peer
func (c *PeerConn) Send(msg *common.WireMessage) {
	go c.doSend(msg)
}

func (c *PeerConn) recv(msg *common.WireMessage) (err error) {
	c.lastRecv = time.Now()
	if (!msg.KeepAlive()) && msg.MessageID() == common.Piece {
		c.rx.AddSample(uint64(msg.Len()))
	}
	log.Debugf("got %d bytes from %s", msg.Len(), c.id)
	err = c.inboundMessage(msg)
	return
}

// send choke
func (c *PeerConn) Choke() {
	if c.usChoke {
		log.Warnf("multiple chokes sent to %s", c.id.String())
	} else {
		log.Debugf("choke peer %s", c.id.String())
		c.Send(common.NewWireMessage(common.Choke, nil))
		c.usChoke = true
	}
}

// send unchoke
func (c *PeerConn) Unchoke() {
	if c.usChoke {
		log.Debugf("unchoke peer %s", c.id.String())
		c.Send(common.NewWireMessage(common.UnChoke, nil))
		c.usChoke = false
	}
}

func (c *PeerConn) gotDownload(p *common.PieceData) {
	c.access.Lock()
	var downloading []*common.PieceRequest
	for _, r := range c.downloading {
		if r.Matches(p) {
			c.t.pt.handlePieceData(p)
		} else {
			downloading = append(downloading, r)
		}
	}
	c.downloading = downloading
	c.access.Unlock()
}

func (c *PeerConn) cancelDownload(req *common.PieceRequest) {
	c.access.Lock()
	var downloading []*common.PieceRequest
	for _, r := range c.downloading {
		if r.Equals(req) {
			c.t.pt.canceledRequest(r)
		} else {
			downloading = append(downloading, r)
		}
	}
	c.downloading = downloading
	c.access.Unlock()
}

func (c *PeerConn) numDownloading() int {
	c.access.Lock()
	i := len(c.downloading)
	c.access.Unlock()
	return i
}

func (c *PeerConn) queueDownload(req *common.PieceRequest) {
	if c.closing {
		c.clearDownloading()
		return
	}
	c.access.Lock()
	c.downloading = append(c.downloading, req)
	log.Debugf("ask %s for %d %d %d", c.id.String(), req.Index, req.Begin, req.Length)
	c.Send(req.ToWireMessage())
	c.access.Unlock()
}

func (c *PeerConn) clearDownloading() {
	c.access.Lock()
	for _, r := range c.downloading {
		c.t.pt.canceledRequest(r)
	}
	c.downloading = []*common.PieceRequest{}
	c.access.Unlock()
}

func (c *PeerConn) HasPiece(piece uint32) bool {
	if c.bf == nil {
		// no bitfield
		return false
	}
	return c.bf.Has(piece)
}

// return true if this peer is choking us otherwise return false
func (c *PeerConn) RemoteChoking() bool {
	return c.peerChoke
}

// return true if we are choking the remote peer otherwise return false
func (c *PeerConn) Chocking() bool {
	return c.usChoke
}

func (c *PeerConn) remoteUnchoke() {
	if !c.peerChoke {
		log.Warnf("remote peer %s sent multiple unchokes", c.id.String())
	}
	c.peerChoke = false
	log.Debugf("%s unchoked us", c.id.String())
}

func (c *PeerConn) remoteChoke() {
	if c.peerChoke {
		log.Warnf("remote peer %s sent multiple chokes", c.id.String())
	}
	c.peerChoke = true
	log.Debugf("%s choked us", c.id.String())
}

func (c *PeerConn) markInterested() {
	c.peerInterested = true
	log.Debugf("%s is interested", c.id.String())
}

func (c *PeerConn) markNotInterested() {
	c.peerInterested = false
	log.Debugf("%s is not interested", c.id.String())
}

func (c *PeerConn) Close() {
	if c.closing {
		return
	}
	c.closing = true
	for _, r := range c.downloading {
		c.t.pt.canceledRequest(r)
	}
	c.downloading = nil
	c.keepalive.Stop()
	log.Debugf("%s closing connection", c.id.String())
	c.c.Close()
	if c.inbound {
		c.t.removeIBConn(c)
	} else {
		c.t.removeOBConn(c)
	}
}

// run read loop
func (c *PeerConn) runReader() {
	err := common.ReadWireMessages(c.c, c.recv)
	if err != nil {
		log.Debugf("PeerConn() reader failed: %s", err.Error())
	}
	c.Close()
}

func (c *PeerConn) inboundMessage(msg *common.WireMessage) (err error) {

	if msg.KeepAlive() {
		log.Debugf("keepalive from %s", c.id)
		return
	}
	msgid := msg.MessageID()
	log.Debugf("%s from %s", msgid.String(), c.id.String())
	if msgid == common.BitField {
		isnew := false
		if c.bf == nil {
			isnew = true
		}
		c.bf = bittorrent.NewBitfield(c.t.MetaInfo().Info.NumPieces(), msg.Payload())
		log.Debugf("got bitfield from %s", c.id.String())
		// TODO: determine if we are really interested
		m := common.NewInterested()
		c.Send(m)
		if isnew {
			c.usInterested = true
			c.Unchoke()
			if c.ourOpts != nil {
				c.Send(c.ourOpts.ToWireMessage())
			}
			go c.runDownload()
		}
		return
	}
	if msgid == common.Choke {
		c.remoteChoke()
	}
	if msgid == common.UnChoke {
		c.remoteUnchoke()
	}
	if msgid == common.Interested {
		c.markInterested()
	}
	if msgid == common.NotInterested {
		c.markNotInterested()
	}
	if msgid == common.Request {
		ev := msg.GetPieceRequest()
		c.t.handlePieceRequest(c, ev)
	}
	if msgid == common.Piece {
		d := msg.GetPieceData()
		if d == nil {
			log.Warnf("invalid piece data message from %s", c.id.String())
			c.Close()
		} else {
			c.gotDownload(d)
		}
	}

	if msgid == common.Have && c.bf != nil {
		// update bitfield
		idx := msg.GetHave()
		c.bf.Set(idx)
	}
	if msgid == common.Cancel {
		// TODO: check validity
		r := msg.GetPieceRequest()
		c.t.pt.canceledRequest(r)
	}
	if msgid == common.Extended {
		// handle extended options
		opts := extensions.FromWireMessage(msg)
		if opts == nil {
			log.Warnf("failed to parse extended options for %s", c.id.String())
		} else {
			c.handleExtendedOpts(opts)
		}
	}
	return
}

// handles an inbound pex message
func (c *PeerConn) handlePEX(m interface{}) {

	pex, ok := m.(map[string]interface{})
	if ok {
		var added interface{}
		added, ok = pex["added"]
		if ok {
			c.handlePEXAdded(added)
		}
		added, ok = pex["added.f"]
		if ok {
			c.handlePEXAddedf(added)
		}
	} else {
		log.Errorf("invalid pex message: %q", m)
	}
}

// handle inbound PEX message payload
func (c *PeerConn) handlePEXAdded(m interface{}) {
	var peers []common.Peer
	msg := m.(string)
	l := len(msg) / 32
	for l > 0 {
		var p common.Peer
		// TODO: bounds check
		copy(p.Compact[:], msg[(l-1)*32:l*32])
		l--
		peers = append(peers, p)
	}
	c.t.addPeers(peers)
}

func (c *PeerConn) handlePEXAddedf(m interface{}) {
	// TODO: implement this
}

func (c *PeerConn) SupportsPEX() bool {
	if c.theirOpts == nil {
		return false
	}
	return c.theirOpts.PEX()
}

func (c *PeerConn) sendPEX(connected, disconnected []byte) {
	id := c.theirOpts.Extensions[extensions.PeerExchange.String()]
	msg := extensions.NewPEX(id, connected, disconnected)
	c.Send(msg.ToWireMessage())
}

func (c *PeerConn) handleExtendedOpts(opts *extensions.Message) {
	log.Debugf("got extended opts from %s: %s", c.id.String(), opts)
	if opts.ID == 0 {
		// handshake
		if c.theirOpts == nil {
			c.theirOpts = opts.Copy()
		} else {
			log.Warnf("got multiple extended option handshakes from %s", c.id.String())
		}
	} else {
		// extended data
		if c.theirOpts == nil {
			log.Warnf("%s gave unexpected extended message %d", c.id.String(), opts.ID)
		} else {
			// lookup the extension number
			ext, ok := c.theirOpts.Lookup(opts.ID)
			if ok {
				if ext == extensions.PeerExchange.String() {
					// this is PEX message
					c.handlePEX(opts.Payload)
				} else if ext == extensions.XDHT.String() {
					// xdht message
					err := c.t.xdht.HandleMessage(opts, c.id)
					if err != nil {
						log.Warnf("error handling xdht message from %s: %s", c.id.String(), err.Error())
					}
				}
			} else {
				log.Warnf("peer %s gave us extension for message we do not have id=%d", c.id.String(), opts.ID)
			}
		}
	}
}

func (c *PeerConn) sendKeepAlive() {
	tm := time.Now().Add(0 - (time.Minute * 2))
	if c.lastSend.Before(tm) {
		log.Debugf("send keepalive to %s", c.id.String())
		c.doSend(common.KeepAlive())
	}
}

// run download loop
func (c *PeerConn) runDownload() {
	for !c.t.Done() && !c.closing {
		if c.RemoteChoking() {
			log.Debugf("will not download this tick, %s is choking", c.id.String())
			time.Sleep(time.Second)
			continue
		}
		// pending request
		p := c.numDownloading()
		if p >= c.MaxParalellRequests {
			time.Sleep(time.Second)
			continue
		}
		r := c.t.pt.nextRequestForDownload(c.bf)
		if r == nil {
			log.Debugf("no next piece to download for %s", c.id.String())
			time.Sleep(time.Second)
		} else {
			c.queueDownload(r)
		}
	}
	if c.closing {
		c.Close()
	} else {
		log.Debugf("peer %s is 'done'", c.id.String())
	}

	// done downloading
	if c.Done != nil {
		c.Done()
	}
}
<commit_msg>remove keepalive ticker<commit_after>package swarm

import (
	"net"
	"sync"
	"time"
	"xd/lib/bittorrent"
	"xd/lib/bittorrent/extensions"
	"xd/lib/common"
	"xd/lib/log"
	"xd/lib/util"
)

const DefaultMaxParallelRequests = 8

// a peer connection
type PeerConn struct {
	inbound             bool
	closing             bool
	c                   net.Conn
	id                  common.PeerID
	t                   *Torrent
	sendMtx             sync.Mutex
	bf                  *bittorrent.Bitfield
	peerChoke           bool
	peerInterested      bool
	usChoke             bool
	usInterested        bool
	Done                func()
	lastSend            time.Time
	tx                  util.Rate
	lastRecv            time.Time
	rx                  util.Rate
	downloading         []*common.PieceRequest
	ourOpts             *extensions.Message
	theirOpts           *extensions.Message
	MaxParalellRequests int
	access              sync.Mutex
}

// get stats for this connection
func (c *PeerConn) Stats() (st *PeerConnStats) {
	st = new(PeerConnStats)
	st.TX = c.tx.Rate()
	st.RX = c.rx.Rate()
	st.Addr = c.c.RemoteAddr().String()
	st.ID = c.id.String()
	return
}

func makePeerConn(c net.Conn, t *Torrent, id common.PeerID, ourOpts *extensions.Message) *PeerConn {
	p := new(PeerConn)
	p.c = c
	p.t = t
	p.ourOpts = ourOpts
	p.peerChoke = true
	p.usChoke = true
	copy(p.id[:], id[:])
	p.MaxParalellRequests = t.MaxRequests
	p.downloading = []*common.PieceRequest{}
	return p
}

func (c *PeerConn) start() {
	go c.runReader()
	go c.runKeepAlive()
	go c.tickStats()
}

func (c *PeerConn) runKeepAlive() {
	for !c.closing {
		time.Sleep(time.Second)
		c.sendKeepAlive()
	}
}

func (c *PeerConn) tickStats() {
	for !c.closing {
		time.Sleep(time.Second)
		c.tx.Tick()
		c.rx.Tick()
	}
}

func (c *PeerConn) doSend(msg *common.WireMessage) {
	if !c.closing && msg != nil {
		c.sendMtx.Lock()
		now := time.Now()
		c.lastSend = now
		if c.RemoteChoking() && msg.MessageID() == common.Request {
			// drop
			log.Debugf("drop request because choke")
			r := msg.GetPieceRequest()
			c.cancelDownload(r)
		} else {
			log.Debugf("writing %d bytes", msg.Len())
			err := msg.Send(c.c)
			if err == nil {
				if msg.MessageID() == common.Piece {
					c.tx.AddSample(uint64(msg.Len()))
				}
				log.Debugf("wrote message %s %d bytes", msg.MessageID(), msg.Len())
			} else {
				log.Debugf("write error: %s", err.Error())
			}
		}
		c.sendMtx.Unlock()
	}
}

// queue a send of a bittorrent wire message to this peer
func (c *PeerConn) Send(msg *common.WireMessage) {
	go c.doSend(msg)
}

func (c *PeerConn) recv(msg *common.WireMessage) (err error) {
	c.lastRecv = time.Now()
	if (!msg.KeepAlive()) && msg.MessageID() == common.Piece {
		c.rx.AddSample(uint64(msg.Len()))
	}
	log.Debugf("got %d bytes from %s", msg.Len(), c.id)
	err = c.inboundMessage(msg)
	return
}

// send choke
func (c *PeerConn) Choke() {
	if c.usChoke {
		log.Warnf("multiple chokes sent to %s", c.id.String())
	} else {
		log.Debugf("choke peer %s", c.id.String())
		c.Send(common.NewWireMessage(common.Choke, nil))
		c.usChoke = true
	}
}

// send unchoke
func (c *PeerConn) Unchoke() {
	if c.usChoke {
		log.Debugf("unchoke peer %s", c.id.String())
		c.Send(common.NewWireMessage(common.UnChoke, nil))
		c.usChoke = false
	}
}

func (c *PeerConn) gotDownload(p *common.PieceData) {
	c.access.Lock()
	var downloading []*common.PieceRequest
	for _, r := range c.downloading {
		if r.Matches(p) {
			c.t.pt.handlePieceData(p)
		} else {
			downloading = append(downloading, r)
		}
	}
	c.downloading = downloading
	c.access.Unlock()
}

func (c *PeerConn) cancelDownload(req *common.PieceRequest) {
	c.access.Lock()
	var downloading []*common.PieceRequest
	for _, r := range c.downloading {
		if r.Equals(req) {
			c.t.pt.canceledRequest(r)
		} else {
			downloading = append(downloading, r)
		}
	}
	c.downloading = downloading
	c.access.Unlock()
}

func (c *PeerConn) numDownloading() int {
	c.access.Lock()
	i := len(c.downloading)
	c.access.Unlock()
	return i
}

func (c *PeerConn) queueDownload(req *common.PieceRequest) {
	if c.closing {
		c.clearDownloading()
		return
	}
	c.access.Lock()
	c.downloading = append(c.downloading, req)
	log.Debugf("ask %s for %d %d %d", c.id.String(), req.Index, req.Begin, req.Length)
	c.Send(req.ToWireMessage())
	c.access.Unlock()
}

func (c *PeerConn) clearDownloading() {
	c.access.Lock()
	for _, r := range c.downloading {
		c.t.pt.canceledRequest(r)
	}
	c.downloading = []*common.PieceRequest{}
	c.access.Unlock()
}

func (c *PeerConn) HasPiece(piece uint32) bool {
	if c.bf == nil {
		// no bitfield
		return false
	}
	return c.bf.Has(piece)
}

// return true if this peer is choking us otherwise return false
func (c *PeerConn) RemoteChoking() bool {
	return c.peerChoke
}

// return true if we are choking the remote peer otherwise return false
func (c *PeerConn) Chocking() bool {
	return c.usChoke
}

func (c *PeerConn) remoteUnchoke() {
	if !c.peerChoke {
		log.Warnf("remote peer %s sent multiple unchokes", c.id.String())
	}
	c.peerChoke = false
	log.Debugf("%s unchoked us", c.id.String())
}

func (c *PeerConn) remoteChoke() {
	if c.peerChoke {
		log.Warnf("remote peer %s sent multiple chokes", c.id.String())
	}
	c.peerChoke = true
	log.Debugf("%s choked us", c.id.String())
}

func (c *PeerConn) markInterested() {
	c.peerInterested = true
	log.Debugf("%s is interested", c.id.String())
}

func (c *PeerConn) markNotInterested() {
	c.peerInterested = false
	log.Debugf("%s is not interested", c.id.String())
}

func (c *PeerConn) Close() {
	if c.closing {
		return
	}
	c.closing = true
	for _, r := range c.downloading {
		c.t.pt.canceledRequest(r)
	}
	c.downloading = nil
	log.Debugf("%s closing connection", c.id.String())
	c.c.Close()
	if c.inbound {
		c.t.removeIBConn(c)
	} else {
		c.t.removeOBConn(c)
	}
}

// run read loop
func (c *PeerConn) runReader() {
	err := common.ReadWireMessages(c.c, c.recv)
	if err != nil {
		log.Debugf("PeerConn() reader failed: %s", err.Error())
	}
	c.Close()
}

func (c *PeerConn) inboundMessage(msg *common.WireMessage) (err error) {

	if msg.KeepAlive() {
		log.Debugf("keepalive from %s", c.id)
		return
	}
	msgid := msg.MessageID()
	log.Debugf("%s from %s", msgid.String(), c.id.String())
	if msgid == common.BitField {
		isnew := false
		if c.bf == nil {
			isnew = true
		}
		c.bf = bittorrent.NewBitfield(c.t.MetaInfo().Info.NumPieces(), msg.Payload())
		log.Debugf("got bitfield from %s", c.id.String())
		// TODO: determine if we are really interested
		m := common.NewInterested()
		c.Send(m)
		if isnew {
			c.usInterested = true
			c.Unchoke()
			if c.ourOpts != nil {
				c.Send(c.ourOpts.ToWireMessage())
			}
			go c.runDownload()
		}
		return
	}
	if msgid == common.Choke {
		c.remoteChoke()
	}
	if msgid == common.UnChoke {
		c.remoteUnchoke()
	}
	if msgid == common.Interested {
		c.markInterested()
	}
	if msgid == common.NotInterested {
		c.markNotInterested()
	}
	if msgid == common.Request {
		ev := msg.GetPieceRequest()
		c.t.handlePieceRequest(c, ev)
	}
	if msgid == common.Piece {
		d := msg.GetPieceData()
		if d == nil {
			log.Warnf("invalid piece data message from %s", c.id.String())
			c.Close()
		} else {
			c.gotDownload(d)
		}
	}

	if msgid == common.Have && c.bf != nil {
		// update bitfield
		idx := msg.GetHave()
		c.bf.Set(idx)
	}
	if msgid == common.Cancel {
		// TODO: check validity
		r := msg.GetPieceRequest()
		c.t.pt.canceledRequest(r)
	}
	if msgid == common.Extended {
		// handle extended options
		opts := extensions.FromWireMessage(msg)
		if opts == nil {
			log.Warnf("failed to parse extended options for %s", c.id.String())
		} else {
			c.handleExtendedOpts(opts)
		}
	}
	return
}

// handles an inbound pex message
func (c *PeerConn) handlePEX(m interface{}) {

	pex, ok := m.(map[string]interface{})
	if ok {
		var added interface{}
		added, ok = pex["added"]
		if ok {
			c.handlePEXAdded(added)
		}
		added, ok = pex["added.f"]
		if ok {
			c.handlePEXAddedf(added)
		}
	} else {
		log.Errorf("invalid pex message: %q", m)
	}
}

// handle inbound PEX message payload
func (c *PeerConn) handlePEXAdded(m interface{}) {
	var peers []common.Peer
	msg := m.(string)
	l := len(msg) / 32
	for l > 0 {
		var p common.Peer
		// TODO: bounds check
		copy(p.Compact[:], msg[(l-1)*32:l*32])
		l--
		peers = append(peers, p)
	}
	c.t.addPeers(peers)
}

func (c *PeerConn) handlePEXAddedf(m interface{}) {
	// TODO: implement this
}

func (c *PeerConn) SupportsPEX() bool {
	if c.theirOpts == nil {
		return false
	}
	return c.theirOpts.PEX()
}

func (c *PeerConn) sendPEX(connected, disconnected []byte) {
	id := c.theirOpts.Extensions[extensions.PeerExchange.String()]
	msg := extensions.NewPEX(id, connected, disconnected)
	c.Send(msg.ToWireMessage())
}

func (c *PeerConn) handleExtendedOpts(opts *extensions.Message) {
	log.Debugf("got extended opts from %s: %s", c.id.String(), opts)
	if opts.ID == 0 {
		// handshake
		if c.theirOpts == nil {
			c.theirOpts = opts.Copy()
		} else {
			log.Warnf("got multiple extended option handshakes from %s", c.id.String())
		}
	} else {
		// extended data
		if c.theirOpts == nil {
			log.Warnf("%s gave unexpected extended message %d", c.id.String(), opts.ID)
		} else {
			// lookup the extension number
			ext, ok := c.theirOpts.Lookup(opts.ID)
			if ok {
				if ext == extensions.PeerExchange.String() {
					// this is PEX message
					c.handlePEX(opts.Payload)
				} else if ext == extensions.XDHT.String() {
					// xdht message
					err := c.t.xdht.HandleMessage(opts, c.id)
					if err != nil {
						log.Warnf("error handling xdht message from %s: %s", c.id.String(), err.Error())
					}
				}
			} else {
				log.Warnf("peer %s gave us extension for message we do not have id=%d", c.id.String(), opts.ID)
			}
		}
	}
}

func (c *PeerConn) sendKeepAlive() {
	tm := time.Now().Add(0 - (time.Minute * 2))
	if c.lastSend.Before(tm) {
		log.Debugf("send keepalive to %s", c.id.String())
		c.doSend(common.KeepAlive())
	}
}

// run download loop
func (c *PeerConn) runDownload() {
	for !c.t.Done() && !c.closing {
		if c.RemoteChoking() {
			log.Debugf("will not download this tick, %s is choking", c.id.String())
			time.Sleep(time.Second)
			continue
		}
		// pending request
		p := c.numDownloading()
		if p >= c.MaxParalellRequests {
			time.Sleep(time.Second)
			continue
		}
		r := c.t.pt.nextRequestForDownload(c.bf)
		if r == nil {
			log.Debugf("no next piece to download for %s", c.id.String())
			time.Sleep(time.Second)
		} else {
			c.queueDownload(r)
		}
	}
	if c.closing {
		c.Close()
	} else {
		log.Debugf("peer %s is 'done'", c.id.String())
	}

	// done downloading
	if c.Done != nil {
		c.Done()
	}
}
<|endoftext|>


<commit_before>package server

import (
	"github.com/stretchr/testify/assert"
	"net"
	"os"
	"testing"
)

func TestParsingOfEnvironmentVariables(t *testing.T) {
	a := assert.New(t)

	originalArgs := os.Args
	os.Args = []string{os.Args[0]}
	defer func() { os.Args = originalArgs }()

	// given: some environment variables
	os.Setenv("GUBLE_HTTP_LISTEN", "http_listen")
	defer os.Unsetenv("GOBBLER_HTTP_LISTEN")

	os.Setenv("GUBLE_LOG", "debug")
	defer os.Unsetenv("GUBLE_LOG")

	os.Setenv("GUBLE_ENV", "dev")
	defer os.Unsetenv("GOBBLER_ENV")

	os.Setenv("GUBLE_PROFILE", "mem")
	defer os.Unsetenv("GUBLE_PROFILE")

	os.Setenv("GUBLE_KVS", "kvs-backend")
	defer os.Unsetenv("GUBLE_KVS")

	os.Setenv("GUBLE_STORAGE_PATH", os.TempDir())
	defer os.Unsetenv("GUBLE_STORAGE_PATH")

	os.Setenv("GUBLE_HEALTH_ENDPOINT", "health_endpoint")
	defer os.Unsetenv("GUBLE_HEALTH_ENDPOINT")

	os.Setenv("GUBLE_METRICS_ENDPOINT", "metrics_endpoint")
	defer os.Unsetenv("GUBLE_METRICS_ENDPOINT")

	os.Setenv("GUBLE_PROMETHEUS_ENDPOINT", "prometheus_endpoint")
	defer os.Unsetenv("GUBLE_PROMETHEUS_ENDPOINT")

	os.Setenv("GUBLE_TOGGLES_ENDPOINT", "toggles_endpoint")
	defer os.Unsetenv("GUBLE_TOGGLES_ENDPOINT")

	os.Setenv("GUBLE_MS", "ms-backend")
	defer os.Unsetenv("GUBLE_MS")

	os.Setenv("GUBLE_WS", "true")
	defer os.Unsetenv("GUBLE_WS")

	os.Setenv("GUBLE_WS_PREFIX", "/wstream/")
	defer os.Unsetenv("GUBLE_WS_PREFIX")

	os.Setenv("GUBLE_FCM", "true")
	defer os.Unsetenv("GUBLE_FCM")

	os.Setenv("GUBLE_FCM_API_KEY", "fcm-api-key")
	defer os.Unsetenv("GUBLE_FCM_API_KEY")

	os.Setenv("GUBLE_FCM_WORKERS", "3")
	defer os.Unsetenv("GUBLE_FCM_WORKERS")

	os.Setenv("GUBLE_APNS", "true")
	defer os.Unsetenv("GUBLE_APNS")

	os.Setenv("GUBLE_APNS_PRODUCTION", "true")
	defer os.Unsetenv("GUBLE_APNS_PRODUCTION")

	os.Setenv("GUBLE_APNS_CERT_BYTES", "00ff")
	defer os.Unsetenv("GUBLE_APNS_CERT_BYTES")

	os.Setenv("GUBLE_APNS_CERT_PASSWORD", "rotten")
	defer os.Unsetenv("GUBLE_APNS_CERT_PASSWORD")

	os.Setenv("GUBLE_APNS_APP_TOPIC", "com.myapp")
	defer os.Unsetenv("GUBLE_APNS_APP_TOPIC")

	os.Setenv("GUBLE_NODE_ID", "1")
	defer os.Unsetenv("GUBLE_NODE_ID")

	os.Setenv("GUBLE_NODE_PORT", "10000")
	defer os.Unsetenv("GUBLE_NODE_PORT")

	os.Setenv("GUBLE_PG_HOST", "pg-host")
	defer os.Unsetenv("GUBLE_PG_HOST")

	os.Setenv("GUBLE_PG_PORT", "5432")
	defer os.Unsetenv("GUBLE_PG_PORT")

	os.Setenv("GUBLE_PG_USER", "pg-user")
	defer os.Unsetenv("GUBLE_PG_USER")

	os.Setenv("GUBLE_PG_PASSWORD", "pg-password")
	defer os.Unsetenv("GUBLE_PG_PASSWORD")

	os.Setenv("GUBLE_PG_DBNAME", "pg-dbname")
	defer os.Unsetenv("GUBLE_PG_DBNAME")

	os.Setenv("GUBLE_NODE_REMOTES", "127.0.0.1:8080 127.0.0.1:20002")
	defer os.Unsetenv("GUBLE_NODE_REMOTES")

	os.Setenv("GUBLE_KAFKA_BROKERS", "127.0.0.1:9092 127.0.0.1:9091")
	defer os.Unsetenv("GUBLE_KAFKA_BROKERS")

	os.Setenv("GUBLE_SMS_KAFKA_TOPIC", "sms_reporting_topic")
	defer os.Unsetenv("GUBLE_SMS_KAFKA_TOPIC")

	os.Setenv("GUBLE_SMS_TOGGLEABLE", "true")
	defer os.Unsetenv("GUBLE_SMS_TOGGLEABLE")

	// when we parse the arguments from environment variables
	parseConfig()

	// then the parsed parameters are correctly set
	assertArguments(a)
}

func TestParsingArgs(t *testing.T) {
	a := assert.New(t)

	originalArgs := os.Args

	defer func() { os.Args = originalArgs }()

	// given: a command line
	os.Args = []string{os.Args[0],
		"--http", "http_listen",
		"--env", "dev",
		"--log", "debug",
		"--profile", "mem",
		"--storage-path", os.TempDir(),
		"--kvs", "kvs-backend",
		"--ms", "ms-backend",
		"--health-endpoint", "health_endpoint",
		"--metrics-endpoint", "metrics_endpoint",
		"--prometheus-endpoint", "prometheus_endpoint",
		"--toggles-endpoint", "toggles_endpoint",
		"--ws",
		"--ws-prefix", "/wstream/",
		"--fcm",
		"--fcm-api-key", "fcm-api-key",
		"--fcm-workers", "3",
		"--apns",
		"--apns-production",
		"--apns-cert-bytes", "00ff",
		"--apns-cert-password", "rotten",
		"--apns-app-topic", "com.myapp",
		"--node-id", "1",
		"--node-port", "10000",
		"--pg-host", "pg-host",
		"--pg-port", "5432",
		"--pg-user", "pg-user",
		"--pg-password", "pg-password",
		"--pg-dbname", "pg-dbname",
		"--remotes", "127.0.0.1:8080 127.0.0.1:20002",
		"--kafka-brokers", "127.0.0.1:9092 127.0.0.1:9091",
		"--sms-kafka-topic", "sms_reporting_topic",
		"--sms-toggleable",
	}

	// when we parse the arguments from command-line flags
	parseConfig()

	// then the parsed parameters are correctly set
	assertArguments(a)
}

func assertArguments(a *assert.Assertions) {
	a.Equal("http_listen", *Config.HttpListen)
	a.Equal("kvs-backend", *Config.KVS)
	a.Equal(os.TempDir(), *Config.StoragePath)
	a.Equal("ms-backend", *Config.MS)
	a.Equal("health_endpoint", *Config.HealthEndpoint)

	a.Equal("metrics_endpoint", *Config.MetricsEndpoint)
	a.Equal("prometheus_endpoint", *Config.PrometheusEndpoint)
	a.Equal("toggles_endpoint", *Config.TogglesEndpoint)

	a.Equal(true, *Config.WS.Enabled)
	a.Equal("/wstream/", *Config.WS.Prefix)

	a.Equal(true, *Config.FCM.Enabled)
	a.Equal("fcm-api-key", *Config.FCM.APIKey)
	a.Equal(3, *Config.FCM.Workers)

	a.Equal(true, *Config.APNS.Enabled)
	a.Equal(true, *Config.APNS.Production)
	a.Equal([]byte{0, 255}, *Config.APNS.CertificateBytes)
	a.Equal("rotten", *Config.APNS.CertificatePassword)
	a.Equal("com.myapp", *Config.APNS.AppTopic)

	a.Equal(uint8(1), *Config.Cluster.NodeID)
	a.Equal(10000, *Config.Cluster.NodePort)

	a.Equal("pg-host", *Config.Postgres.Host)
	a.Equal(5432, *Config.Postgres.Port)
	a.Equal("pg-user", *Config.Postgres.User)
	a.Equal("pg-password", *Config.Postgres.Password)
	a.Equal("pg-dbname", *Config.Postgres.DbName)

	a.Equal("debug", *Config.Log)
	a.Equal("dev", *Config.EnvName)
	a.Equal("mem", *Config.Profile)

	a.Equal("[127.0.0.1:9092 127.0.0.1:9091]", (*Config.KafkaProducer.Brokers).String())
	a.Equal("sms_reporting_topic", *Config.SMS.KafkaReportingTopic)

	a.Equal(true, *Config.SMS.Toggleable)

	assertClusterRemotes(a)
}

func assertClusterRemotes(a *assert.Assertions) {
	ip1, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
	ip2, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:20002")
	ipList := make(tcpAddrList, 0)
	ipList = append(ipList, ip1)
	ipList = append(ipList, ip2)
	a.Equal(ipList, *Config.Cluster.Remotes)
}

func TestPrefixEnvar(t *testing.T) {
	assert.Equal(t, "GUBLE_SOMEVAR", g("SOMEVAR"))
}<commit_msg>fix test<commit_after>package server

import (
	"github.com/stretchr/testify/assert"
	"net"
	"os"
	"testing"
)

func TestParsingOfEnvironmentVariables(t *testing.T) {
	a := assert.New(t)

	originalArgs := os.Args
	os.Args = []string{os.Args[0]}
	defer func() { os.Args = originalArgs }()

	// given: some environment variables
	os.Setenv("GUBLE_HTTP_LISTEN", "http_listen")
	defer os.Unsetenv("GUBLE_HTTP_LISTEN")

	os.Setenv("GUBLE_LOG", "debug")
	defer os.Unsetenv("GUBLE_LOG")

	os.Setenv("GUBLE_ENV", "dev")
	defer os.Unsetenv("GUBLE_ENV")

	os.Setenv("GUBLE_PROFILE", "mem")
	defer os.Unsetenv("GUBLE_PROFILE")

	os.Setenv("GUBLE_KVS", "kvs-backend")
	defer os.Unsetenv("GUBLE_KVS")

	os.Setenv("GUBLE_STORAGE_PATH", os.TempDir())
	defer os.Unsetenv("GUBLE_STORAGE_PATH")

	os.Setenv("GUBLE_HEALTH_ENDPOINT", "health_endpoint")
	defer os.Unsetenv("GUBLE_HEALTH_ENDPOINT")

	os.Setenv("GUBLE_METRICS_ENDPOINT", "metrics_endpoint")
	defer os.Unsetenv("GUBLE_METRICS_ENDPOINT")

	os.Setenv("GUBLE_PROMETHEUS_ENDPOINT", "prometheus_endpoint")
	defer os.Unsetenv("GUBLE_PROMETHEUS_ENDPOINT")

	os.Setenv("GUBLE_TOGGLES_ENDPOINT", "toggles_endpoint")
	defer os.Unsetenv("GUBLE_TOGGLES_ENDPOINT")

	os.Setenv("GUBLE_MS", "ms-backend")
	defer os.Unsetenv("GUBLE_MS")

	os.Setenv("GUBLE_WS", "true")
	defer os.Unsetenv("GUBLE_WS")

	os.Setenv("GUBLE_WS_PREFIX", "/wstream/")
	defer os.Unsetenv("GUBLE_WS_PREFIX")

	os.Setenv("GUBLE_FCM", "true")
	defer os.Unsetenv("GUBLE_FCM")

	os.Setenv("GUBLE_FCM_API_KEY", "fcm-api-key")
	defer os.Unsetenv("GUBLE_FCM_API_KEY")

	os.Setenv("GUBLE_FCM_WORKERS", "3")
	defer os.Unsetenv("GUBLE_FCM_WORKERS")

	os.Setenv("GUBLE_APNS", "true")
	defer os.Unsetenv("GUBLE_APNS")

	os.Setenv("GUBLE_APNS_PRODUCTION", "true")
	defer os.Unsetenv("GUBLE_APNS_PRODUCTION")

	os.Setenv("GUBLE_APNS_CERT_BYTES", "00ff")
	defer os.Unsetenv("GUBLE_APNS_CERT_BYTES")

	os.Setenv("GUBLE_APNS_CERT_PASSWORD", "rotten")
	defer os.Unsetenv("GUBLE_APNS_CERT_PASSWORD")

	os.Setenv("GUBLE_APNS_APP_TOPIC", "com.myapp")
	defer os.Unsetenv("GUBLE_APNS_APP_TOPIC")

	os.Setenv("GUBLE_NODE_ID", "1")
	defer os.Unsetenv("GUBLE_NODE_ID")

	os.Setenv("GUBLE_NODE_PORT", "10000")
	defer os.Unsetenv("GUBLE_NODE_PORT")

	os.Setenv("GUBLE_PG_HOST", "pg-host")
	defer os.Unsetenv("GUBLE_PG_HOST")

	os.Setenv("GUBLE_PG_PORT", "5432")
	defer os.Unsetenv("GUBLE_PG_PORT")

	os.Setenv("GUBLE_PG_USER", "pg-user")
	defer os.Unsetenv("GUBLE_PG_USER")

	os.Setenv("GUBLE_PG_PASSWORD", "pg-password")
	defer os.Unsetenv("GUBLE_PG_PASSWORD")

	os.Setenv("GUBLE_PG_DBNAME", "pg-dbname")
	defer os.Unsetenv("GUBLE_PG_DBNAME")

	os.Setenv("GUBLE_NODE_REMOTES", "127.0.0.1:8080 127.0.0.1:20002")
	defer os.Unsetenv("GUBLE_NODE_REMOTES")

	os.Setenv("GUBLE_KAFKA_BROKERS", "127.0.0.1:9092 127.0.0.1:9091")
	defer os.Unsetenv("GUBLE_KAFKA_BROKERS")

	os.Setenv("GUBLE_SMS_KAFKA_TOPIC", "sms_reporting_topic")
	defer os.Unsetenv("GUBLE_SMS_KAFKA_TOPIC")

	os.Setenv("GUBLE_SMS_TOGGLEABLE", "true")
	defer os.Unsetenv("GUBLE_SMS_TOGGLEABLE")

	// when we parse the arguments from environment variables
	parseConfig()

	// then the parsed parameters are correctly set
	assertArguments(a)
}

func TestParsingArgs(t *testing.T) {
	a := assert.New(t)

	originalArgs := os.Args

	defer func() { os.Args = originalArgs }()

	// given: a command line
	os.Args = []string{os.Args[0],
		"--http", "http_listen",
		"--env", "dev",
		"--log", "debug",
		"--profile", "mem",
		"--storage-path", os.TempDir(),
		"--kvs", "kvs-backend",
		"--ms", "ms-backend",
		"--health-endpoint", "health_endpoint",
		"--metrics-endpoint", "metrics_endpoint",
		"--prometheus-endpoint", "prometheus_endpoint",
		"--toggles-endpoint", "toggles_endpoint",
		"--ws",
		"--ws-prefix", "/wstream/",
		"--fcm",
		"--fcm-api-key", "fcm-api-key",
		"--fcm-workers", "3",
		"--apns",
		"--apns-production",
		"--apns-cert-bytes", "00ff",
		"--apns-cert-password", "rotten",
		"--apns-app-topic", "com.myapp",
		"--node-id", "1",
		"--node-port", "10000",
		"--pg-host", "pg-host",
		"--pg-port", "5432",
		"--pg-user", "pg-user",
		"--pg-password", "pg-password",
		"--pg-dbname", "pg-dbname",
		"--remotes", "127.0.0.1:8080 127.0.0.1:20002",
		"--kafka-brokers", "127.0.0.1:9092 127.0.0.1:9091",
		"--sms-kafka-topic", "sms_reporting_topic",
		"--sms-toggleable",
	}

	// when we parse the arguments from command-line flags
	parseConfig()

	// then the parsed parameters are correctly set
	assertArguments(a)
}

func assertArguments(a *assert.Assertions) {
	a.Equal("http_listen", *Config.HttpListen)
	a.Equal("kvs-backend", *Config.KVS)
	a.Equal(os.TempDir(), *Config.StoragePath)
	a.Equal("ms-backend", *Config.MS)
	a.Equal("health_endpoint", *Config.HealthEndpoint)

	a.Equal("metrics_endpoint", *Config.MetricsEndpoint)
	a.Equal("prometheus_endpoint", *Config.PrometheusEndpoint)
	a.Equal("toggles_endpoint", *Config.TogglesEndpoint)

	a.Equal(true, *Config.WS.Enabled)
	a.Equal("/wstream/", *Config.WS.Prefix)

	a.Equal(true, *Config.FCM.Enabled)
	a.Equal("fcm-api-key", *Config.FCM.APIKey)
	a.Equal(3, *Config.FCM.Workers)

	a.Equal(true, *Config.APNS.Enabled)
	a.Equal(true, *Config.APNS.Production)
	a.Equal([]byte{0, 255}, *Config.APNS.CertificateBytes)
	a.Equal("rotten", *Config.APNS.CertificatePassword)
	a.Equal("com.myapp", *Config.APNS.AppTopic)

	a.Equal(uint8(1), *Config.Cluster.NodeID)
	a.Equal(10000, *Config.Cluster.NodePort)

	a.Equal("pg-host", *Config.Postgres.Host)
	a.Equal(5432, *Config.Postgres.Port)
	a.Equal("pg-user", *Config.Postgres.User)
	a.Equal("pg-password", *Config.Postgres.Password)
	a.Equal("pg-dbname", *Config.Postgres.DbName)

	a.Equal("debug", *Config.Log)
	a.Equal("dev", *Config.EnvName)
	a.Equal("mem", *Config.Profile)

	a.Equal("[127.0.0.1:9092 127.0.0.1:9091]", (*Config.KafkaProducer.Brokers).String())
	a.Equal("sms_reporting_topic", *Config.SMS.KafkaReportingTopic)

	a.Equal(true, *Config.SMS.Toggleable)

	assertClusterRemotes(a)
}

func assertClusterRemotes(a *assert.Assertions) {
	ip1, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
	ip2, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:20002")
	ipList := make(tcpAddrList, 0)
	ipList = append(ipList, ip1)
	ipList = append(ipList, ip2)
	a.Equal(ipList, *Config.Cluster.Remotes)
}

func TestPrefixEnvar(t *testing.T) {
	assert.Equal(t, "GUBLE_SOMEVAR", g("SOMEVAR"))
}
<|endoftext|>


<commit_before>package utils

import "testing"
import "github.com/stretchr/testify/assert"

func TestHashingPlaintext(t *testing.T) {
	s, err := NewScrypt()
	assert.NotNil(t, s)
	assert.Nil(t, err)

	hash, err := s.HashPlaintext("my lil plaintext")
	assert.NotEmpty(t, hash)
	assert.Contains(t, hash, "32768@8@1")
	assert.Nil(t, err)
}

func TestMatchingPlaintext(t *testing.T) {
	s, err := NewScrypt()
	assert.NotNil(t, s)
	assert.Nil(t, err)

	hash, err := s.HashPlaintext("my lil plaintext")
	assert.NotEmpty(t, hash)
	assert.Contains(t, hash, "32768@8@1")
	assert.Nil(t, err)

	s2, err := LoadScryptFromHash(hash)
	assert.NotNil(t, s2)
	assert.Nil(t, err)

	match, err := s2.MatchesPlaintext("my lil plaintext")
	assert.True(t, match)
	assert.Nil(t, err)
}

func TestBarfOnLoadingGarbage(t *testing.T) {
    s, err := LoadScryptFromHash("123")
	assert.Nil(t, s)
	assert.NotNil(t, err)

    s, err = LoadScryptFromHash("asd")
	assert.Nil(t, s)
	assert.NotNil(t, err)

	s, err = LoadScryptFromHash("123@456")
	assert.Nil(t, s)
	assert.NotNil(t, err)

    s, err = LoadScryptFromHash("asd@lol")
	assert.Nil(t, s)
	assert.NotNil(t, err)

    s, err = LoadScryptFromHash("123@456@789")
	assert.Nil(t, s)
	assert.NotNil(t, err)

    s, err = LoadScryptFromHash("asd@lol@wtf")
	assert.Nil(t, s)
	assert.NotNil(t, err)

    s, err = LoadScryptFromHash("123@456@789@foo@bar")
	assert.Nil(t, s)
	assert.NotNil(t, err)

    s, err = LoadScryptFromHash("asd@lol@wtf@bbq@kfc")
	assert.Nil(t, s)
	assert.NotNil(t, err)

}

func BenchmarkPlaintextEncryption(b *testing.B) {
	s, _ := NewScrypt()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		s.HashPlaintext("my lil plaintext")
	}
}
<commit_msg>Tweaked test to hopefully cover more.<commit_after>package utils

import "testing"
import "github.com/stretchr/testify/assert"

func TestHashingPlaintext(t *testing.T) {
	s, err := NewScrypt()
	assert.NotNil(t, s)
	assert.Nil(t, err)

	hash, err := s.HashPlaintext("my lil plaintext")
	assert.NotEmpty(t, hash)
	assert.Contains(t, hash, "32768@8@1")
	assert.Nil(t, err)
}

func TestMatchingPlaintext(t *testing.T) {
	s, err := NewScrypt()
	assert.NotNil(t, s)
	assert.Nil(t, err)

	hash, err := s.HashPlaintext("my lil plaintext")
	assert.NotEmpty(t, hash)
	assert.Contains(t, hash, "32768@8@1")
	assert.Nil(t, err)

	s2, err := LoadScryptFromHash(hash)
	assert.NotNil(t, s2)
	assert.Nil(t, err)

	match, err := s2.MatchesPlaintext("my lil plaintext")
	assert.True(t, match)
	assert.Nil(t, err)
}

func TestBarfOnLoadingGarbage(t *testing.T) {
    s, err := LoadScryptFromHash("123")
	assert.Nil(t, s)
	assert.NotNil(t, err)

	s, err = LoadScryptFromHash("123@456")
	assert.Nil(t, s)
	assert.NotNil(t, err)

    s, err = LoadScryptFromHash("123@456@789")
	assert.Nil(t, s)
	assert.NotNil(t, err)

    s, err = LoadScryptFromHash("123@456@789@012")
	assert.Nil(t, s)
	assert.NotNil(t, err)

    s, err = LoadScryptFromHash("asd@lol@wtf@bbq@kfc")
	assert.Nil(t, s)
	assert.NotNil(t, err)

    s, err = LoadScryptFromHash("123@asd@lol@bbq@kfc")
	assert.Nil(t, s)
	assert.NotNil(t, err)

    s, err = LoadScryptFromHash("123@456@wtf@bbq@kfc")
	assert.Nil(t, s)
	assert.NotNil(t, err)

    s, err = LoadScryptFromHash("123@456@789@bbq@kfc")
	assert.Nil(t, s)
	assert.NotNil(t, err)
}

func BenchmarkPlaintextEncryption(b *testing.B) {
	s, _ := NewScrypt()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		s.HashPlaintext("my lil plaintext")
	}
}
<|endoftext|>


<commit_before>package service

import (
	"fmt"
	"github.com/ninjasphere/app-presets/model"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/logger"
)

type PresetsService struct {
	Model       *model.Presets
	Save        func(*model.Presets)
	Conn        *ninja.Connection
	Log         *logger.Logger
	initialized bool
}

func (ps *PresetsService) Init() error {
	if ps.Log == nil {
		return fmt.Errorf("illegal state: no logger")
	}
	if ps.Model == nil {
		return fmt.Errorf("illegal state: Model is nil")
	}
	if ps.Save == nil {
		return fmt.Errorf("illegal state: Save is nil")
	}
	if ps.Conn == nil {
		return fmt.Errorf("illegal state: Conn is nil")
	}
	ps.initialized = true
	return nil
}

func (ps *PresetsService) Destroy() error {
	ps.initialized = false
	return nil
}

func (ps *PresetsService) checkInit() {
	if ps.Log == nil {
		ps.Log = logger.GetLogger("com.ninja.app-presets")
	}
	if !ps.initialized {
		ps.Log.Fatalf("illegal state: the service is not initialized")
	}
}

// see: http://schema.ninjablocks.com/service/presets#listPresetable
func (ps *PresetsService) ListPresetable(scope string) ([]*model.ThingState, error) {
	ps.checkInit()
	return make([]*model.ThingState, 0, 0), fmt.Errorf("unimplemented function: ListPresetable")
}

// see: http://schema.ninjablocks.com/service/presets#fetchScenes
func (ps *PresetsService) FetchScenes(scope string) ([]model.Scene, error) {
	return make([]model.Scene, 0, 0), fmt.Errorf("unimplemented function: FetchScenes")
}

// see: http://schema.ninjablocks.com/service/presets#fetchScene
func (ps *PresetsService) FetchScene(id string) (*model.Scene, error) {
	return nil, fmt.Errorf("unimplemented function: FetchScene")
}

// see: http://schema.ninjablocks.com/service/presets#storeScene
func (ps *PresetsService) StoreScene(model *model.Scene) error {
	return fmt.Errorf("unimplemented function: StoreScene")
}

// see: http://schema.ninjablocks.com/service/presets#applyScene
func (ps *PresetsService) ApplyScene(id string) error {
	return fmt.Errorf("unimplemented function: ApplyScene")
}
<commit_msg>Implement PresetService FetchScenes.<commit_after>package service

import (
	"fmt"
	"github.com/ninjasphere/app-presets/model"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/logger"
)

type PresetsService struct {
	Model       *model.Presets
	Save        func(*model.Presets)
	Conn        *ninja.Connection
	Log         *logger.Logger
	initialized bool
}

func (ps *PresetsService) Init() error {
	if ps.Log == nil {
		return fmt.Errorf("illegal state: no logger")
	}
	if ps.Model == nil {
		return fmt.Errorf("illegal state: Model is nil")
	}
	if ps.Save == nil {
		return fmt.Errorf("illegal state: Save is nil")
	}
	if ps.Conn == nil {
		return fmt.Errorf("illegal state: Conn is nil")
	}
	ps.initialized = true
	return nil
}

func (ps *PresetsService) Destroy() error {
	ps.initialized = false
	return nil
}

func (ps *PresetsService) checkInit() {
	if ps.Log == nil {
		ps.Log = logger.GetLogger("com.ninja.app-presets")
	}
	if !ps.initialized {
		ps.Log.Fatalf("illegal state: the service is not initialized")
	}
}

// see: http://schema.ninjablocks.com/service/presets#listPresetable
func (ps *PresetsService) ListPresetable(scope string) ([]*model.ThingState, error) {
	ps.checkInit()
	return make([]*model.ThingState, 0, 0), fmt.Errorf("unimplemented function: ListPresetable")
}

// see: http://schema.ninjablocks.com/service/presets#fetchScenes
func (ps *PresetsService) FetchScenes(scope string) ([]*model.Scene, error) {
	ps.checkInit()
	collect := make([]*model.Scene, 0, 0)
	for _, m := range ps.Model.Scenes {
		if m.Scope == scope {
			collect = append(collect, m)
		}
	}
	return collect, nil
}

// see: http://schema.ninjablocks.com/service/presets#fetchScene
func (ps *PresetsService) FetchScene(id string) (*model.Scene, error) {
	return nil, fmt.Errorf("unimplemented function: FetchScene")
}

// see: http://schema.ninjablocks.com/service/presets#storeScene
func (ps *PresetsService) StoreScene(model *model.Scene) error {
	return fmt.Errorf("unimplemented function: StoreScene")
}

// see: http://schema.ninjablocks.com/service/presets#applyScene
func (ps *PresetsService) ApplyScene(id string) error {
	return fmt.Errorf("unimplemented function: ApplyScene")
}
<|endoftext|>


<commit_before>package httpclient

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
)

type Encoding string

const (
	EncodingJSON = "JSON"
)

type RequestData struct {
	Method          string
	Path            string
	Params          url.Values
	FullURL         string // optional
	Headers         http.Header
	ReqReader       io.Reader
	ReqEncoding     Encoding
	ReqValue        interface{}
	ExpectedStatus  []int
	IgnoreRedirects bool
	RespEncoding    Encoding
	RespValue       interface{}
}

type InvalidStatusError struct {
	Expected []int
	Got      int
}

func (e InvalidStatusError) Error() string {
	return fmt.Sprintf("Invalid response status! Got %d, expected %d", e.Got, e.Expected)
}

type HTTPClient struct {
	BaseURL   *url.URL
	Headers   http.Header
	Client    *http.Client
	PostHooks map[int]func(*http.Request, *http.Response) error
}

func New() (httpClient *HTTPClient) {
	return &HTTPClient{
		Client:    HttpClient,
		Headers:   make(http.Header),
		PostHooks: make(map[int]func(*http.Request, *http.Response) error),
	}
}

func Insecure() (httpClient *HTTPClient) {
	return &HTTPClient{
		Client:    InsecureHttpClient,
		Headers:   make(http.Header),
		PostHooks: make(map[int]func(*http.Request, *http.Response) error),
	}
}

func (c *HTTPClient) SetPostHook(onStatus int, hook func(*http.Request, *http.Response) error) {
	c.PostHooks[onStatus] = hook
}

func (c *HTTPClient) buildURL(req *RequestData) string {
	if req.FullURL != "" {
		return req.FullURL
	}

	bu := c.BaseURL

	u := url.URL{
		Scheme: bu.Scheme,
		Host:   bu.Host,
		Path:   bu.Path + req.Path,
	}

	if req.Params != nil {
		u.RawQuery = req.Params.Encode()
	}

	return u.String()
}

func (c *HTTPClient) setHeaders(req *RequestData, httpReq *http.Request) {

	switch req.ReqEncoding {
	case EncodingJSON:
		httpReq.Header.Set("Content-Type", "application/json")
	}

	switch req.RespEncoding {
	case EncodingJSON:
		httpReq.Header.Set("Accept", "application/json")
	}

	if c.Headers != nil {
		for key, values := range c.Headers {
			for _, value := range values {
				httpReq.Header.Set(key, value)
			}
		}
	}

	if req.Headers != nil {
		for key, values := range req.Headers {
			for _, value := range values {
				httpReq.Header.Set(key, value)
			}
		}
	}
}

func (c *HTTPClient) checkStatus(req *RequestData, response *http.Response) (err error) {
	if req.ExpectedStatus != nil {
		statusOk := false

		for _, status := range req.ExpectedStatus {
			if response.StatusCode == status {
				statusOk = true
			}
		}

		if !statusOk {
			err = InvalidStatusError{req.ExpectedStatus, response.StatusCode}
			return
		}
	}

	return
}

func (c *HTTPClient) unmarshalResponse(req *RequestData, response *http.Response) (err error) {
	var buf []byte

	switch req.RespEncoding {
	case EncodingJSON:
		defer response.Body.Close()

		if buf, err = ioutil.ReadAll(response.Body); err != nil {
			return
		}

		err = json.Unmarshal(buf, req.RespValue)

		return
	}

	switch req.RespValue.(type) {
	case *[]byte:
		defer response.Body.Close()

		if buf, err = ioutil.ReadAll(response.Body); err != nil {
			return
		}

		respVal := req.RespValue.(*[]byte)
		*respVal = buf

		return
	}

	return
}

func (c *HTTPClient) marshalRequest(req *RequestData) (err error) {
	if req.ReqValue != nil && req.ReqEncoding != "" && req.ReqReader == nil {
		var buf []byte
		buf, err = json.Marshal(req.ReqValue)
		if err != nil {
			return
		}
		req.ReqReader = bytes.NewReader(buf)
	}
	return
}

func (c *HTTPClient) runPostHook(req *http.Request, response *http.Response) (err error) {
	hook, ok := c.PostHooks[response.StatusCode]

	if ok {
		err = hook(req, response)
	}

	return
}

func (c *HTTPClient) Request(req *RequestData) (response *http.Response, err error) {
	reqURL := c.buildURL(req)

	err = c.marshalRequest(req)

	if err != nil {
		return
	}

	r, err := http.NewRequest(req.Method, reqURL, req.ReqReader)

	if err != nil {
		return
	}

	c.setHeaders(req, r)

	if req.IgnoreRedirects {
		transport := c.Client.Transport

		if transport == nil {
			transport = http.DefaultTransport
		}

		response, err = transport.RoundTrip(r)
	} else {
		response, err = c.Client.Do(r)
	}

	if err != nil {
		return
	}

	if err = c.runPostHook(r, response); err != nil {
		return
	}

	if err = c.checkStatus(req, response); err != nil {
		return
	}

	if err = c.unmarshalResponse(req, response); err != nil {
		return
	}

	return
}
<commit_msg>added RespConsume<commit_after>package httpclient

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
)

type Encoding string

const (
	EncodingJSON = "JSON"
)

type RequestData struct {
	Method          string
	Path            string
	Params          url.Values
	FullURL         string // optional
	Headers         http.Header
	ReqReader       io.Reader
	ReqEncoding     Encoding
	ReqValue        interface{}
	ExpectedStatus  []int
	IgnoreRedirects bool
	RespEncoding    Encoding
	RespValue       interface{}
	RespConsume     bool
}

type InvalidStatusError struct {
	Expected []int
	Got      int
}

func (e InvalidStatusError) Error() string {
	return fmt.Sprintf("Invalid response status! Got %d, expected %d", e.Got, e.Expected)
}

type HTTPClient struct {
	BaseURL   *url.URL
	Headers   http.Header
	Client    *http.Client
	PostHooks map[int]func(*http.Request, *http.Response) error
}

func New() (httpClient *HTTPClient) {
	return &HTTPClient{
		Client:    HttpClient,
		Headers:   make(http.Header),
		PostHooks: make(map[int]func(*http.Request, *http.Response) error),
	}
}

func Insecure() (httpClient *HTTPClient) {
	return &HTTPClient{
		Client:    InsecureHttpClient,
		Headers:   make(http.Header),
		PostHooks: make(map[int]func(*http.Request, *http.Response) error),
	}
}

func (c *HTTPClient) SetPostHook(onStatus int, hook func(*http.Request, *http.Response) error) {
	c.PostHooks[onStatus] = hook
}

func (c *HTTPClient) buildURL(req *RequestData) string {
	if req.FullURL != "" {
		return req.FullURL
	}

	bu := c.BaseURL

	u := url.URL{
		Scheme: bu.Scheme,
		Host:   bu.Host,
		Path:   bu.Path + req.Path,
	}

	if req.Params != nil {
		u.RawQuery = req.Params.Encode()
	}

	return u.String()
}

func (c *HTTPClient) setHeaders(req *RequestData, httpReq *http.Request) {

	switch req.ReqEncoding {
	case EncodingJSON:
		httpReq.Header.Set("Content-Type", "application/json")
	}

	switch req.RespEncoding {
	case EncodingJSON:
		httpReq.Header.Set("Accept", "application/json")
	}

	if c.Headers != nil {
		for key, values := range c.Headers {
			for _, value := range values {
				httpReq.Header.Set(key, value)
			}
		}
	}

	if req.Headers != nil {
		for key, values := range req.Headers {
			for _, value := range values {
				httpReq.Header.Set(key, value)
			}
		}
	}
}

func (c *HTTPClient) checkStatus(req *RequestData, response *http.Response) (err error) {
	if req.ExpectedStatus != nil {
		statusOk := false

		for _, status := range req.ExpectedStatus {
			if response.StatusCode == status {
				statusOk = true
			}
		}

		if !statusOk {
			err = InvalidStatusError{req.ExpectedStatus, response.StatusCode}
			return
		}
	}

	return
}

func (c *HTTPClient) unmarshalResponse(req *RequestData, response *http.Response) (err error) {
	var buf []byte

	switch req.RespEncoding {
	case EncodingJSON:
		defer response.Body.Close()

		if buf, err = ioutil.ReadAll(response.Body); err != nil {
			return
		}

		err = json.Unmarshal(buf, req.RespValue)

		return
	}

	switch req.RespValue.(type) {
	case *[]byte:
		defer response.Body.Close()

		if buf, err = ioutil.ReadAll(response.Body); err != nil {
			return
		}

		respVal := req.RespValue.(*[]byte)
		*respVal = buf

		return
	}

	if req.RespConsume {
		defer response.Body.Close()
		ioutil.ReadAll(response.Body)
	}

	return
}

func (c *HTTPClient) marshalRequest(req *RequestData) (err error) {
	if req.ReqValue != nil && req.ReqEncoding != "" && req.ReqReader == nil {
		var buf []byte
		buf, err = json.Marshal(req.ReqValue)
		if err != nil {
			return
		}
		req.ReqReader = bytes.NewReader(buf)
	}
	return
}

func (c *HTTPClient) runPostHook(req *http.Request, response *http.Response) (err error) {
	hook, ok := c.PostHooks[response.StatusCode]

	if ok {
		err = hook(req, response)
	}

	return
}

func (c *HTTPClient) Request(req *RequestData) (response *http.Response, err error) {
	reqURL := c.buildURL(req)

	err = c.marshalRequest(req)

	if err != nil {
		return
	}

	r, err := http.NewRequest(req.Method, reqURL, req.ReqReader)

	if err != nil {
		return
	}

	c.setHeaders(req, r)

	if req.IgnoreRedirects {
		transport := c.Client.Transport

		if transport == nil {
			transport = http.DefaultTransport
		}

		response, err = transport.RoundTrip(r)
	} else {
		response, err = c.Client.Do(r)
	}

	if err != nil {
		return
	}

	if err = c.runPostHook(r, response); err != nil {
		return
	}

	if err = c.checkStatus(req, response); err != nil {
		return
	}

	if err = c.unmarshalResponse(req, response); err != nil {
		return
	}

	return
}
<|endoftext|>


<commit_before>package check

import "fmt"

var baseTemplate = `# Save as MyRule.yml on your StylesPath
# See https://valelint.github.io/styles/#check-types for more info
%s
`
var existenceTemplate = `extends: existence
# "%s" will be replaced by the active token
message: "found '%s'!"
ignorecase: false
# "suggestion", "warning" or "error"
level: warning
tokens:
  - XXX
  - FIXME
  - TODO
  - NOTE`

var substitutionTemplate = `extends: substitution
message: Consider using '%s' instead of '%s'
ignorecase: false
# "suggestion", "warning" or "error"
level: warning
# swap maps tokens in form of bad: good
swap:
  abundance: plenty
  accelerate: speed up`

var checkToTemplate = map[string]string{
	"existence":    existenceTemplate,
	"substitution": substitutionTemplate,
}

// GetTemplate makes a template for the given extension point.
func GetTemplate(name string) string {
	if template, ok := checkToTemplate[name]; ok {
		return fmt.Sprintf(baseTemplate, template)
	}
	return ""
}
<commit_msg>feat: finish templates<commit_after>package check

import "fmt"

var baseTemplate = `# Save as MyRule.yml on your StylesPath
# See https://valelint.github.io/styles/#check-types for more info
# "suggestion", "warning" or "error"
level: warning
# Text describing this rule (generally longer than 'message').
description: '...'
# A link the source or reference.
link: '...'
%s`

var existenceTemplate = `extends: existence
# "%s" will be replaced by the active token
message: "found '%s'!"
ignorecase: false
tokens:
  - XXX
  - FIXME
  - TODO
  - NOTE`

var substitutionTemplate = `extends: substitution
message: Consider using '%s' instead of '%s'
ignorecase: false
# swap maps tokens in form of bad: good
swap:
  abundance: plenty
  accelerate: speed up`

var occurrenceTemplate = `extends: occurrence
message: "More than 3 commas!"
# Here, we're counting the number of times a comma appears in a sentence.
# If it occurs more than 3 times, we'll flag it.
scope: sentence
ignorecase: false
max: 3
token: ','`

var conditionalTemplate = `extends: conditional
message: "'%s' has no definition"
scope: text
ignorecase: false
# Ensures that the existence of 'first' implies the existence of 'second'.
first: \b([A-Z]{3,5})\b
second: (?:\b[A-Z][a-z]+ )+\(([A-Z]{3,5})\)
# ... with the exception of these:
exceptions:
  - ABC
  - ADD`

var consistencyTemplate = `extends: consistency
message: "Inconsistent spelling of '%s'"
scope: text
ignorecase: true
nonword: false
# We only want one of these to appear.
either:
  advisor: adviser
  centre: center`

var repetitionTemplate = `extends: repetition
message: "'%s' is repeated!"
scope: paragraph
ignorecase: false
# Will flag repeated occurances of the same token (e.g., "this this").
tokens:
  - '[^\s]+'`

var checkToTemplate = map[string]string{
	"existence":    existenceTemplate,
	"substitution": substitutionTemplate,
	"occurrence":   occurrenceTemplate,
	"conditional":  conditionalTemplate,
	"consistency":  consistencyTemplate,
	"repetition":   repetitionTemplate,
}

// GetTemplate makes a template for the given extension point.
func GetTemplate(name string) string {
	if template, ok := checkToTemplate[name]; ok {
		return fmt.Sprintf(baseTemplate, template)
	}
	return ""
}
<|endoftext|>


<commit_before>package service

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

const (
	// Service defaults.
	DefaultStartTimeout = 1 * time.Second
	DefaultStartRetries = 3
	DefaultStopSignal   = syscall.SIGINT
	DefaultStopTimeout  = 5 * time.Second
	DefaultStopRestart  = true

	// Service commands.
	Start    = "start"
	Stop     = "stop"
	Restart  = "restart"
	Shutdown = "shutdown"

	// Service states.
	Starting = "starting"
	Running  = "running"
	Stopping = "stopping"
	Stopped  = "stopped"
	Exited   = "exited"
	Backoff  = "backoff"
)

// Command is sent to a Service to initiate a state change.
type Command struct {
	Name     string
	Response chan<- Response
}

// respond creates and sends a command Response.
func (cmd Command) respond(service *Service, err error) {
	if cmd.Response != nil {
		cmd.Response <- Response{service, cmd.Name, err}
	}
}

// Response contains the result of a Command.
type Response struct {
	Service *Service
	Name    string
	Error   error
}

// Success returns True if the Command was successful.
func (r Response) Success() bool {
	return r.Error == nil
}

// Event is sent by a Service on a state change.
type Event struct {
	Service *Service
	State   string
}

// Service represents a controllable process. Exported fields may be set to configure the service.
type Service struct {
	Directory    string         // The process's working directory. Defaults to the current directory.
	Environment  []string       // The environment of the process. Defaults to nil which indicatesA the current environment.
	StartTimeout time.Duration  // How long the process has to run before it's considered Running.
	StartRetries int            // How many times to restart a process if it fails to start. Defaults to 3.
	StopSignal   syscall.Signal // The signal to send when stopping the process. Defaults to SIGINT.
	StopTimeout  time.Duration  // How long to wait for a process to stop before sending a SIGKILL. Defaults to 5s.
	StopRestart  bool           // Whether or not to restart the process if it exits unexpectedly. Defaults to true.
	Stdout       io.Writer      // Where to send the process's stdout. Defaults to /dev/null.
	Stderr       io.Writer      // Where to send the process's stderr. Defaults to /dev/null.
	args         []string       // The command line of the process to run.
	command      *exec.Cmd      // The os/exec command running the process.
	state        string         // The state of the Service.
}

// New creates a new service with the default configution.
func NewService(args []string) (svc *Service, err error) {
	if cwd, err := os.Getwd(); err == nil {
		svc = &Service{
			cwd,
			nil,
			DefaultStartTimeout,
			DefaultStartRetries,
			DefaultStopSignal,
			DefaultStopTimeout,
			DefaultStopRestart,
			nil,
			nil,
			args,
			nil,
			Stopped,
		}
	}
	return
}

// State gets the current state of the service.
func (s Service) State() string {
	return s.state
}

// Pid gets the PID of the service or 0 if not Running or Stopping.
func (s Service) Pid() int {
	if s.state != Running && s.state != Stopping {
		return 0
	}
	return s.command.Process.Pid
}

func (s Service) makeCommand() *exec.Cmd {
	cmd := exec.Command(s.args[0], s.args[1:]...)
	cmd.Stdout = s.Stdout
	cmd.Stderr = s.Stderr
	cmd.Stdin = nil
	cmd.Env = s.Environment
	cmd.Dir = s.Directory
	return cmd
}

func (s *Service) Run(commands <-chan Command, events chan<- Event) {
	var lastCommand *Command
	states := make(chan string)
	quit := make(chan bool, 2)
	kill := make(chan int, 2)
	retries := 0

	defer func() {
		close(states)
		close(quit)
		close(kill)
	}()

	sendEvent := func(state string) {
		s.state = state
		events <- Event{s, state}
	}

	sendInvalidCmd := func(cmd *Command, state string) {
		if cmd != nil {
			cmd.respond(s, errors.New(fmt.Sprintf("invalid state transition: %s -> %s", s.state, state)))
		}
	}

	start := func(cmd *Command) {
		if s.state != Stopped && s.state != Exited && s.state != Backoff {
			sendInvalidCmd(cmd, Starting)
			return
		}

		sendEvent(Starting)
		go func() {
			s.command = s.makeCommand()
			startTime := time.Now()
			if err := s.command.Start(); err == nil { //TODO: Don't swallow this error.
				states <- Running
				s.command.Wait()
				if time.Now().Sub(startTime) > s.StartTimeout {
					states <- Backoff
					return
				}
			}
			states <- Exited
		}()
	}

	stop := func(cmd *Command) {
		if s.state != Running {
			sendInvalidCmd(cmd, Stopping)
			return
		}

		sendEvent(Stopping)
		pid := s.Pid()
		s.command.Process.Signal(s.StopSignal) //TODO: Check for error.
		go func() {
			time.Sleep(s.StopTimeout)
			defer func() {
				if err := recover(); err != nil {
					if _, ok := err.(runtime.Error); !ok {
						panic(err)
					}
				}
			}()
			kill <- pid
		}()
	}

	shutdown := func(cmd *Command, lastCmd *Command) {
		if lastCmd != nil {
			lastCmd.respond(s, errors.New("service is shutting down"))
		}
		if s.state == Stopped || s.state == Exited {
			quit <- true
		} else if s.state == Running {
			stop(cmd)
		}
	}

	onRunning := func(cmd *Command) {
		sendEvent(Running)
		if cmd != nil {
			switch cmd.Name {
			case Start:
				fallthrough
			case Restart:
				cmd.respond(s, nil)
			case Shutdown:
				stop(cmd)
			}
		}
	}

	onStopped := func(cmd *Command) {
		sendEvent(Stopped)
		if cmd != nil {
			switch cmd.Name {
			case Restart:
				start(cmd)
			case Stop:
				cmd.respond(s, nil)
			case Shutdown:
				quit <- true
			}
		}
	}

	onExited := func(cmd *Command) {
		sendEvent(Exited)
		if s.StopRestart {
			start(cmd)
		}
	}

	onBackoff := func(cmd *Command) {
		if retries < s.StartRetries {
			sendEvent(Backoff)
			start(cmd)
			retries++
		} else {
			sendEvent(Exited)
			retries = 0
		}
	}

loop:
	for {
		select {
		case state := <-states:
			// running, exited
			switch state {
			case Running:
				onRunning(lastCommand)
			case Exited:
				if s.state == Stopping {
					onStopped(lastCommand)
				} else {
					onExited(lastCommand)
				}
			case Backoff:
				onBackoff(lastCommand)
			}
			if lastCommand != nil {
				if lastCommand.Name == Restart && s.state == Running {
					lastCommand = nil
				} else if lastCommand.Name != Restart && lastCommand.Name != Shutdown {
					lastCommand = nil
				}
			}
		case command := <-commands:
			if lastCommand == nil || lastCommand.Name != Shutdown { // Shutdown cannot be overriden!
				switch command.Name {
				case Start:
					start(&command)
				case Stop:
					stop(&command)
				case Restart:
					stop(&command)
				case Shutdown:
					shutdown(&command, lastCommand)
				}
				lastCommand = &command
			} else {
				command.respond(s, errors.New("service is shutting down"))
			}
		case <-quit:
			if lastCommand != nil {
				lastCommand.respond(s, nil)
			}
			break loop
		case pid := <-kill:
			if pid == s.Pid() {
				s.command.Process.Kill() //TODO: Check for error.
			}
		}
	}
}
<commit_msg>Send errors on exit and start failures.<commit_after>package service

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

const (
	// Service defaults.
	DefaultStartTimeout = 1 * time.Second
	DefaultStartRetries = 3
	DefaultStopSignal   = syscall.SIGINT
	DefaultStopTimeout  = 5 * time.Second
	DefaultStopRestart  = true

	// Service commands.
	Start    = "start"
	Stop     = "stop"
	Restart  = "restart"
	Shutdown = "shutdown"

	// Service states.
	Starting = "starting"
	Running  = "running"
	Stopping = "stopping"
	Stopped  = "stopped"
	Exited   = "exited"
	Backoff  = "backoff"
)

// Command is sent to a Service to initiate a state change.
type Command struct {
	Name     string
	Response chan<- Response
}

// respond creates and sends a command Response.
func (cmd Command) respond(service *Service, err error) {
	if cmd.Response != nil {
		cmd.Response <- Response{service, cmd.Name, err}
	}
}

// Response contains the result of a Command.
type Response struct {
	Service *Service
	Name    string
	Error   error
}

// Success returns True if the Command was successful.
func (r Response) Success() bool {
	return r.Error == nil
}

// Event is sent by a Service on a state change.
type Event struct {
	Service *Service // The service from which the event originated.
	State   string   // The new state of the service.
	Error   error    // An error indicating why the service is in Exited or Backoff.
}

// ExitError indicated why the service entered an Exited or Backoff state.
type ExitError string

// Error returns the error message of the ExitError.
func (err ExitError) Error() string {
	return string(err)
}

// Service represents a controllable process. Exported fields may be set to configure the service.
type Service struct {
	Directory    string         // The process's working directory. Defaults to the current directory.
	Environment  []string       // The environment of the process. Defaults to nil which indicatesA the current environment.
	StartTimeout time.Duration  // How long the process has to run before it's considered Running.
	StartRetries int            // How many times to restart a process if it fails to start. Defaults to 3.
	StopSignal   syscall.Signal // The signal to send when stopping the process. Defaults to SIGINT.
	StopTimeout  time.Duration  // How long to wait for a process to stop before sending a SIGKILL. Defaults to 5s.
	StopRestart  bool           // Whether or not to restart the process if it exits unexpectedly. Defaults to true.
	Stdout       io.Writer      // Where to send the process's stdout. Defaults to /dev/null.
	Stderr       io.Writer      // Where to send the process's stderr. Defaults to /dev/null.
	args         []string       // The command line of the process to run.
	command      *exec.Cmd      // The os/exec command running the process.
	state        string         // The state of the Service.
}

// New creates a new service with the default configution.
func NewService(args []string) (svc *Service, err error) {
	if cwd, err := os.Getwd(); err == nil {
		svc = &Service{
			cwd,
			nil,
			DefaultStartTimeout,
			DefaultStartRetries,
			DefaultStopSignal,
			DefaultStopTimeout,
			DefaultStopRestart,
			nil,
			nil,
			args,
			nil,
			Stopped,
		}
	}
	return
}

// State gets the current state of the service.
func (s Service) State() string {
	return s.state
}

// Pid gets the PID of the service or 0 if not Running or Stopping.
func (s Service) Pid() int {
	if s.state != Running && s.state != Stopping {
		return 0
	}
	return s.command.Process.Pid
}

func (s Service) makeCommand() *exec.Cmd {
	cmd := exec.Command(s.args[0], s.args[1:]...)
	cmd.Stdout = s.Stdout
	cmd.Stderr = s.Stderr
	cmd.Stdin = nil
	cmd.Env = s.Environment
	cmd.Dir = s.Directory
	return cmd
}

func (s *Service) Run(commands <-chan Command, events chan<- Event) {
	type ProcessState struct {
		State string
		Error error
	}

	var lastCommand *Command
	states := make(chan ProcessState)
	quit := make(chan bool, 2)
	kill := make(chan int, 2)
	retries := 0

	defer func() {
		close(states)
		close(quit)
		close(kill)
	}()

	sendEvent := func(state string, err error) {
		s.state = state
		events <- Event{s, state, err}
	}

	sendInvalidCmd := func(cmd *Command, state string) {
		if cmd != nil {
			cmd.respond(s, errors.New(fmt.Sprintf("invalid state transition: %s -> %s", s.state, state)))
		}
	}

	start := func(cmd *Command) {
		if s.state != Stopped && s.state != Exited && s.state != Backoff {
			sendInvalidCmd(cmd, Starting)
			return
		}

		sendEvent(Starting, nil)
		go func() {
			s.command = s.makeCommand()
			startTime := time.Now()
			if err := s.command.Start(); err == nil {
				states <- ProcessState{Running, nil}
				exitErr := s.command.Wait()

				msg := ""
				if time.Now().Sub(startTime) < s.StartTimeout {
					if exitErr == nil {
						msg = "process exited prematurely with success"
					} else {
						msg = fmt.Sprintf("process exited prematurely with failure: %s", exitErr)
					}
					states <- ProcessState{Backoff, ExitError(msg)}
				} else {
					if exitErr == nil {
						msg = "process exited normally with success"
					} else {
						msg = fmt.Sprintf("process exited normally with failure: %s", exitErr)
					}
					states <- ProcessState{Exited, ExitError(msg)}
				}
			} else {
				states <- ProcessState{Exited, err}
			}
		}()
	}

	stop := func(cmd *Command) {
		if s.state != Running {
			sendInvalidCmd(cmd, Stopping)
			return
		}

		sendEvent(Stopping, nil)
		pid := s.Pid()
		s.command.Process.Signal(s.StopSignal) //TODO: Check for error.
		go func() {
			time.Sleep(s.StopTimeout)
			defer func() {
				if err := recover(); err != nil {
					if _, ok := err.(runtime.Error); !ok {
						panic(err)
					}
				}
			}()
			kill <- pid
		}()
	}

	shutdown := func(cmd *Command, lastCmd *Command) {
		if lastCmd != nil {
			lastCmd.respond(s, errors.New("service is shutting down"))
		}
		if s.state == Stopped || s.state == Exited {
			quit <- true
		} else if s.state == Running {
			stop(cmd)
		}
	}

	onRunning := func(cmd *Command) {
		sendEvent(Running, nil)
		if cmd != nil {
			switch cmd.Name {
			case Start:
				fallthrough
			case Restart:
				cmd.respond(s, nil)
			case Shutdown:
				stop(cmd)
			}
		}
	}

	onStopped := func(cmd *Command) {
		sendEvent(Stopped, nil)
		if cmd != nil {
			switch cmd.Name {
			case Restart:
				start(cmd)
			case Stop:
				cmd.respond(s, nil)
			case Shutdown:
				quit <- true
			}
		}
	}

	onExited := func(cmd *Command, err error) {
		sendEvent(Exited, err)
		if s.StopRestart {
			start(cmd)
		}
	}

	onBackoff := func(cmd *Command, err error) {
		if retries < s.StartRetries {
			sendEvent(Backoff, err)
			start(cmd)
			retries++
		} else {
			sendEvent(Exited, err)
			retries = 0
		}
	}

loop:
	for {
		select {
		case state := <-states:
			// running, exited
			switch state.State {
			case Running:
				onRunning(lastCommand)
			case Exited:
				if s.state == Stopping {
					onStopped(lastCommand)
				} else {
					onExited(lastCommand, state.Error)
				}
			case Backoff:
				onBackoff(lastCommand, state.Error)
			}
			if lastCommand != nil {
				if lastCommand.Name == Restart && s.state == Running {
					lastCommand = nil
				} else if lastCommand.Name != Restart && lastCommand.Name != Shutdown {
					lastCommand = nil
				}
			}
		case command := <-commands:
			if lastCommand == nil || lastCommand.Name != Shutdown {
				switch command.Name {
				case Start:
					start(&command)
				case Stop:
					stop(&command)
				case Restart:
					stop(&command)
				case Shutdown:
					shutdown(&command, lastCommand)
				}
				lastCommand = &command
			} else {
				command.respond(s, errors.New("service is shutting down"))
			}
		case <-quit:
			if lastCommand != nil {
				lastCommand.respond(s, nil)
			}
			break loop
		case pid := <-kill:
			if pid == s.Pid() {
				s.command.Process.Kill() //TODO: Check for error.
			}
		}
	}
}
<|endoftext|>


<commit_before>package check

import (
	"strings"

	"github.com/errata-ai/vale/core"
	"github.com/jdkato/prose/transform"
)

func lower(s string, ignore []string) bool {
	return s == strings.ToLower(s) || core.StringInSlice(s, ignore)
}

func upper(s string, ignore []string) bool {
	return s == strings.ToUpper(s) || core.StringInSlice(s, ignore)
}

func title(s string, ignore []string, tc *transform.TitleConverter) bool {
	count := 0.0
	words := 0.0
	expected := strings.Fields(tc.Title(s))
	for i, word := range strings.Fields(s) {
		if word == expected[i] || core.StringInSlice(word, ignore) {
			count++
		}
		words++
	}
	return (count / words) > 0.8
}

func sentence(s string, ignore []string) bool {
	count := 0.0
	words := 0.0
	for i, w := range strings.Fields(s) {
		if core.StringInSlice(w, ignore) {
			count++
		} else if i == 0 && w != strings.Title(strings.ToLower(w)) {
			return false
		} else if i == 0 || w == strings.ToLower(w) {
			count++
		}
		words++
	}
	return (count / words) > 0.8
}

var varToFunc = map[string]func(string, []string) bool{
	"$lower":    lower,
	"$upper":    upper,
	"$sentence": sentence,
}

var readabilityMetrics = []string{
	"Gunning Fog",
	"Coleman-Liau",
	"Flesch-Kincaid",
	"SMOG",
	"Automated Readability",
}
<commit_msg>refactor: ignore all-caps words in 'capitalization'<commit_after>package check

import (
	"strings"

	"github.com/errata-ai/vale/core"
	"github.com/jdkato/prose/transform"
)

func lower(s string, ignore []string) bool {
	return s == strings.ToLower(s) || core.StringInSlice(s, ignore)
}

func upper(s string, ignore []string) bool {
	return s == strings.ToUpper(s) || core.StringInSlice(s, ignore)
}

func title(s string, ignore []string, tc *transform.TitleConverter) bool {
	count := 0.0
	words := 0.0
	expected := strings.Fields(tc.Title(s))
	for i, word := range strings.Fields(s) {
		if word == expected[i] || core.StringInSlice(word, ignore) {
			count++
		} else if word == strings.ToUpper(word) {
			count++
		}
		words++
	}
	return (count / words) > 0.8
}

func sentence(s string, ignore []string) bool {
	count := 0.0
	words := 0.0
	for i, w := range strings.Fields(s) {
		if core.StringInSlice(w, ignore) || w == strings.ToUpper(w) {
			count++
		} else if i == 0 && w != strings.Title(strings.ToLower(w)) {
			return false
		} else if i == 0 || w == strings.ToLower(w) {
			count++
		}
		words++
	}
	return (count / words) > 0.8
}

var varToFunc = map[string]func(string, []string) bool{
	"$lower":    lower,
	"$upper":    upper,
	"$sentence": sentence,
}

var readabilityMetrics = []string{
	"Gunning Fog",
	"Coleman-Liau",
	"Flesch-Kincaid",
	"SMOG",
	"Automated Readability",
}
<|endoftext|>


<commit_before>package check

import (
	"strings"

	"github.com/jdkato/prose/transform"
	"github.com/xrash/smetrics"
)

func lower(s string) bool { return s == strings.ToLower(s) }
func upper(s string) bool { return s == strings.ToUpper(s) }

func title(s string) bool {
	return smetrics.Jaro(s, transform.Title(s)) > 0.97
}

func sentence(s string) bool {
	count := 0.0
	words := 0.0
	for i, w := range strings.Fields(s) {
		if i > 0 && w == strings.Title(w) {
			count++
		}
		words++
	}
	return (count / words) < 0.4
}

var varToFunc = map[string]func(string) bool{
	"$title":    title,
	"$lower":    lower,
	"$upper":    upper,
	"$sentence": sentence,
}
<commit_msg>fix: call `ToLower` before `Title`<commit_after>package check

import (
	"strings"

	"github.com/jdkato/prose/transform"
	"github.com/xrash/smetrics"
)

func lower(s string) bool { return s == strings.ToLower(s) }
func upper(s string) bool { return s == strings.ToUpper(s) }

func title(s string) bool {
	return smetrics.Jaro(s, transform.Title(s)) > 0.97
}

func sentence(s string) bool {
	count := 0.0
	words := 0.0
	for i, w := range strings.Fields(s) {
		if i > 0 && w == strings.Title(strings.ToLower(w)) {
			count++
		}
		words++
	}
	return (count / words) < 0.4
}

var varToFunc = map[string]func(string) bool{
	"$title":    title,
	"$lower":    lower,
	"$upper":    upper,
	"$sentence": sentence,
}
<|endoftext|>


<commit_before>package checker

import (
	"fmt"
	"io"
	"os"
	"strings"
	"sync"

	"github.com/mackerelio/mackerel-agent/config"
	cli "gopkg.in/urfave/cli.v1"
	yaml "gopkg.in/yaml.v2"
)

var Command = cli.Command{
	Name:  "run-checks",
	Usage: "run check commands in mackerel-agent.conf",
	Description: `
    Execute command of check plugins in mackerel-agent.conf all at once.
    It is used for checking setting and operation of the check plugins.
	The result is output to stdout in TAP format. If any check fails,
	it exits non-zero.
`,
	Action: doRunChecks,
}

func doRunChecks(c *cli.Context) error {
	confFile := c.GlobalString("conf")
	conf, err := config.LoadConfig(confFile)
	if err != nil {
		return err
	}
	checkers := make([]checker, len(conf.CheckPlugins))
	i := 0
	for name, p := range conf.CheckPlugins {
		checkers[i] = &checkPluginChecker{
			name: name,
			cp:   p,
		}
		i++
	}
	return runChecks(checkers, os.Stdout)
}

type result struct {
	Name     string   `yaml:"-"`
	Memo     string   `yaml:"memo,omitempty"`
	Cmd      []string `yaml:"command,flow"`
	Stdout   string   `yaml:"stdout,omitempty"`
	Stderr   string   `yaml:"stderr,omitempty"`
	ExitCode int      `yaml:"exitCode,omitempty"`
	ErrMsg   string   `yaml:"error,omitempty"`
}

func (re *result) ok() bool {
	return re.ExitCode == 0 && re.ErrMsg == ""
}

func (re *result) tapFormat(num int) string {
	okOrNot := "ok"
	if !re.ok() {
		okOrNot = "not ok"
	}
	b, _ := yaml.Marshal(re)
	// indent
	yamlStr := "  " + strings.Replace(strings.TrimSpace(string(b)), "\n", "\n  ", -1)
	return fmt.Sprintf("%s %d - %s\n  ---\n%s\n  ...",
		okOrNot, num, re.Name, yamlStr)
}

type checkPluginChecker struct {
	name string
	cp   *config.CheckPlugin
}

func (cpc *checkPluginChecker) check() *result {
	p := cpc.cp
	stdout, stderr, exitCode, err := p.Command.Run()
	cmd := p.Command.Args
	if len(cmd) == 0 {
		cmd = append(cmd, p.Command.Cmd)
	}
	errMsg := ""
	if err != nil {
		errMsg = err.Error()
	}
	return &result{
		Name:     cpc.name,
		Memo:     p.Memo,
		Cmd:      cmd,
		ExitCode: exitCode,
		Stdout:   strings.TrimSpace(stdout),
		Stderr:   strings.TrimSpace(stderr),
		ErrMsg:   errMsg,
	}
}

type checker interface {
	check() *result
}

func runChecks(checkers []checker, w io.Writer) error {
	ch := make(chan *result)
	total := len(checkers)
	go func() {
		wg := &sync.WaitGroup{}
		wg.Add(total)
		for _, c := range checkers {
			go func(c checker) {
				defer wg.Done()
				ch <- c.check()
			}(c)
		}
		wg.Wait()
		close(ch)
	}()
	fmt.Fprintln(w, "TAP version 13")
	fmt.Fprintf(w, "1..%d\n", total)
	testNum, errNum := 1, 0
	for re := range ch {
		fmt.Fprintln(w, re.tapFormat(testNum))
		testNum++
		if !re.ok() {
			errNum++
		}
	}
	if errNum > 0 {
		return fmt.Errorf("Failed %d/%d tests, %3.2f%% okay",
			errNum, total, float64(100*(total-errNum))/float64(total))
	}
	return nil
}
<commit_msg>add comment doc<commit_after>package checker

import (
	"fmt"
	"io"
	"os"
	"strings"
	"sync"

	"github.com/mackerelio/mackerel-agent/config"
	cli "gopkg.in/urfave/cli.v1"
	yaml "gopkg.in/yaml.v2"
)

// Command is command definition of mkr run-checks
var Command = cli.Command{
	Name:  "run-checks",
	Usage: "run check commands in mackerel-agent.conf",
	Description: `
    Execute command of check plugins in mackerel-agent.conf all at once.
    It is used for checking setting and operation of the check plugins.
	The result is output to stdout in TAP format. If any check fails,
	it exits non-zero.
`,
	Action: doRunChecks,
}

func doRunChecks(c *cli.Context) error {
	confFile := c.GlobalString("conf")
	conf, err := config.LoadConfig(confFile)
	if err != nil {
		return err
	}
	checkers := make([]checker, len(conf.CheckPlugins))
	i := 0
	for name, p := range conf.CheckPlugins {
		checkers[i] = &checkPluginChecker{
			name: name,
			cp:   p,
		}
		i++
	}
	return runChecks(checkers, os.Stdout)
}

type result struct {
	Name     string   `yaml:"-"`
	Memo     string   `yaml:"memo,omitempty"`
	Cmd      []string `yaml:"command,flow"`
	Stdout   string   `yaml:"stdout,omitempty"`
	Stderr   string   `yaml:"stderr,omitempty"`
	ExitCode int      `yaml:"exitCode,omitempty"`
	ErrMsg   string   `yaml:"error,omitempty"`
}

func (re *result) ok() bool {
	return re.ExitCode == 0 && re.ErrMsg == ""
}

func (re *result) tapFormat(num int) string {
	okOrNot := "ok"
	if !re.ok() {
		okOrNot = "not ok"
	}
	b, _ := yaml.Marshal(re)
	// indent
	yamlStr := "  " + strings.Replace(strings.TrimSpace(string(b)), "\n", "\n  ", -1)
	return fmt.Sprintf("%s %d - %s\n  ---\n%s\n  ...",
		okOrNot, num, re.Name, yamlStr)
}

type checkPluginChecker struct {
	name string
	cp   *config.CheckPlugin
}

func (cpc *checkPluginChecker) check() *result {
	p := cpc.cp
	stdout, stderr, exitCode, err := p.Command.Run()
	cmd := p.Command.Args
	if len(cmd) == 0 {
		cmd = append(cmd, p.Command.Cmd)
	}
	errMsg := ""
	if err != nil {
		errMsg = err.Error()
	}
	return &result{
		Name:     cpc.name,
		Memo:     p.Memo,
		Cmd:      cmd,
		ExitCode: exitCode,
		Stdout:   strings.TrimSpace(stdout),
		Stderr:   strings.TrimSpace(stderr),
		ErrMsg:   errMsg,
	}
}

type checker interface {
	check() *result
}

func runChecks(checkers []checker, w io.Writer) error {
	ch := make(chan *result)
	total := len(checkers)
	go func() {
		wg := &sync.WaitGroup{}
		wg.Add(total)
		for _, c := range checkers {
			go func(c checker) {
				defer wg.Done()
				ch <- c.check()
			}(c)
		}
		wg.Wait()
		close(ch)
	}()
	fmt.Fprintln(w, "TAP version 13")
	fmt.Fprintf(w, "1..%d\n", total)
	testNum, errNum := 1, 0
	for re := range ch {
		fmt.Fprintln(w, re.tapFormat(testNum))
		testNum++
		if !re.ok() {
			errNum++
		}
	}
	if errNum > 0 {
		return fmt.Errorf("Failed %d/%d tests, %3.2f%% okay",
			errNum, total, float64(100*(total-errNum))/float64(total))
	}
	return nil
}
<|endoftext|>