<commit_before>package main

import (
	iniconf "code.google.com/p/goconf/conf"
	"encoding/json"
	"fmt"
	// "github.com/gamelost/bot3server/server"
	nsq "github.com/gamelost/go-nsq"
	// irc "github.com/gamelost/goirc/client"
	ms "github.com/mitchellh/mapstructure"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"
)

const (
	GLCD_CONFIG = "glcd.config"
)

var gamestateTopic = ""

type Message struct {
	Name       string
	PlayerName string
	ClientId   string
	Type       string // better way to persist type info?
	Command    string
	Data       interface{}
}

type ZoneInfo struct {
	x int
	y int
}

type Zone struct {
	Id    int
	Name  string
	State *ZoneInfo
}

type PlayerState struct {
	X float64 `mapstructure:"px"`
	Y float64 `mapstructure:"py"`
}

type Heartbeat struct {
	ClientId  string
	Timestamp time.Time
}

/* Players coming in and out */
type PlayerPassport struct {
	Action string
	Avatar string
}

type ErrorMessage string

type WallMessage string

func main() {
	// the quit channel
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	// read in necessary configuration
	configFile, err := iniconf.ReadConfigFile(GLCD_CONFIG)
	if err != nil {
		log.Fatal("Unable to read configuration file. Exiting now.")
	}

	glcd := &GLCD{QuitChan: sigChan}
	glcd.init(configFile)

	// receiving quit shuts down
	<-glcd.QuitChan
}

type GLCClient struct {
	Name      string
	ClientId  string
	State     *PlayerState
	Heartbeat time.Time
}

// struct type for Bot3
type GLCD struct {
	Online     bool
	ConfigFile *iniconf.ConfigFile

	// NSQ input/output
	NSQWriter             *nsq.Writer
	GLCDaemonTopic        *nsq.Reader
	GLCGameStateTopicName string
	GLCDaemonTopicChannel string
	Clients               map[string]*GLCClient

	// game state channels
	HeartbeatChan chan *Heartbeat

	QuitChan chan os.Signal

	MongoSession *mgo.Session
	MongoDB      *mgo.Database
}

func (glcd *GLCD) init(conf *iniconf.ConfigFile) error {

	glcd.ConfigFile = conf
	glcd.Online = false

	glcd.Clients = map[string]*GLCClient{}

	// Connect to Mongo.
	servers, err := glcd.ConfigFile.GetString("mongo", "servers")

	if err != nil {
		return fmt.Errorf("Mongo: No server configured.")
	}

	glcd.MongoSession, err = mgo.Dial(servers)

	if err != nil {
	}

	db, err := glcd.ConfigFile.GetString("mongo", "db")

	if err != nil {
		return fmt.Errorf("Mongo: No database configured.")
	}

	glcd.MongoDB = glcd.MongoSession.DB(db)

	// set up channels
	glcd.HeartbeatChan = make(chan *Heartbeat)

	nsqdAddress, _ := conf.GetString("nsq", "nsqd-address")
	lookupdAddress, _ := conf.GetString("nsq", "lookupd-address")
	glcd.GLCGameStateTopicName, _ = conf.GetString("nsq", "server-topic")

	glcdTopic, _ := conf.GetString("nsq", "glcd-topic")

	// Create the channel, by connecting to lookupd. (TODO; if it doesn't
	// exist. Also do it the right way with a Register command?)
	glcd.NSQWriter = nsq.NewWriter(nsqdAddress)
	glcd.NSQWriter.Publish(glcd.GLCGameStateTopicName, []byte("{\"client\":\"server\"}"))

	// set up reader for glcdTopic
	reader, err := nsq.NewReader(glcdTopic, "main")
	if err != nil {
		glcd.QuitChan <- syscall.SIGINT
	}
	glcd.GLCDaemonTopic = reader
	glcd.GLCDaemonTopic.AddHandler(glcd)
	glcd.GLCDaemonTopic.ConnectToLookupd(lookupdAddress)

	// Spawn goroutine to clear out clients who don't send hearbeats
	// anymore.
	go glcd.CleanupClients()
	go glcd.HandleHeartbeatChannel()

	return nil
}

func (glcd *GLCD) Publish(msg *Message) {
	encodedRequest, _ := json.Marshal(*msg)
	glcd.NSQWriter.Publish(glcd.GLCGameStateTopicName, encodedRequest)
}

func (glcd *GLCD) HandleHeartbeatChannel() {
	for {
		hb := <-glcd.HeartbeatChan
		fmt.Printf("Received heartbeat: %v\n", hb)
	}
}

func (glcd *GLCD) CleanupClients() error {
	for {
		exp := time.Now().Unix()
		<-time.After(time.Second * 10)
		// Expire any clients who haven't sent a heartbeat in the last 10 seconds.
		for k, v := range glcd.Clients {
			if v.Heartbeat.Unix() < exp {
				delete(glcd.Clients, k)
				//glcd.Publish(&Message{Type: "playerPassport", Data: PlayerPassport{Action: "playerGone"}}) // somehow add k to this
			}
		}
	}
}

// Send a zone file update.
func (glcd *GLCD) SendZones() {
	c := glcd.MongoDB.C("zones")
	q := c.Find(nil)

	if q == nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("No zones found")})
	} else {
		var results []interface{}
		err := q.All(&results)
		if err == nil {
			for _, res := range results {
				glcd.Publish(&Message{Type: "zone", Data: res.(string)}) // dump res as a JSON string
			}
		} else {
			glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to fetch zones: %v", err)})
		}
	}
}

func (glcd *GLCD) SendZone(zone *Zone) {
	c := glcd.MongoDB.C("zones")
	query := bson.M{"zone": zone.Name}
	results := c.Find(query)

	if results == nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("No such zone '%s'", zone.Name)})
	} else {
		var res interface{}
		err := results.One(&res)
		if err == nil {
			glcd.Publish(&Message{Type: "zone", Data: res.(string)})
		} else {
			glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to fetch zone: %v", err)})
		}
	}
}

// Send a zone file update.
func (glcd *GLCD) UpdateZone(zone *Zone) {
	query := bson.M{"zone": zone.Name}
	zdata := ZoneInfo{}
	c := glcd.MongoDB.C("zones")
	val := bson.M{"type": "zone", "zdata": zdata, "timestamp": time.Now()}
	change := bson.M{"$set": val}

	err := c.Update(query, change)

	if err == mgo.ErrNotFound {
		val["id"], _ = c.Count()
		change = bson.M{"$set": val}
		err = c.Update(query, change)
	}

	if err != nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to update zone: %v", err)})
	} else {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Updated zone '%s'", zone.Name)})
	}
}

func (glcd *GLCD) HandleMessage(nsqMessage *nsq.Message) error {

	// fmt.Println("-------")
	// fmt.Printf("Received message %s\n\n", nsqMessage.Body)
	// fmt.Println("-------")
	msg := &Message{}

	err := json.Unmarshal(nsqMessage.Body, &msg)

	if err != nil {
		fmt.Printf(err.Error())
	}

	var dataMap map[string]interface{}
	dataMap = msg.Data.(map[string]interface{})

	if msg.Command == "playerPassport" {
		//		HandlePassport(msg.Data)
	} else if msg.Command == "playerState" {
		var ps PlayerState
		//fmt.Printf("Data is: %v, %v\n", psmap["px"], psmap["py"])
		err := ms.Decode(dataMap, &ps)
		if err != nil {
			fmt.Println(err.Error())
		}
		fmt.Printf("Player state: %+v\n", ps)
	} else if msg.Command == "zone" {
		//		HandleZoneUpdate(msg.Data)
	} else if msg.Command == "wall" {
		//		HandleWallMessage(msg.Data)
	} else if msg.Command == "heartbeat" {
		hb := &Heartbeat{}
		hb.ClientId = msg.Name
		fmt.Printf("heartbeat: %+v", hb)
		glcd.HeartbeatChan <- hb
	} else if msg.Command == "error" {
		//		HandleError(msg.Data)
	} else {
		// log.Printf("Unknown Message Type: %s", msg.Type)
	}

	return nil
}
<commit_msg>finishing up heartbeat functionality<commit_after>package main

import (
	iniconf "code.google.com/p/goconf/conf"
	"encoding/json"
	"fmt"
	// "github.com/gamelost/bot3server/server"
	nsq "github.com/gamelost/go-nsq"
	// irc "github.com/gamelost/goirc/client"
	ms "github.com/mitchellh/mapstructure"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"
)

const (
	GLCD_CONFIG = "glcd.config"
)

var gamestateTopic = ""

type Message struct {
	Name       string
	PlayerName string
	ClientId   string
	Type       string // better way to persist type info?
	Command    string
	Data       interface{}
}

type ZoneInfo struct {
	x int
	y int
}

type Zone struct {
	Id    int
	Name  string
	State *ZoneInfo
}

type PlayerState struct {
	X float64 `mapstructure:"px"`
	Y float64 `mapstructure:"py"`
}

type Heartbeat struct {
	ClientId  string
	Timestamp time.Time
}

/* Players coming in and out */
type PlayerPassport struct {
	Action string
	Avatar string
}

type ErrorMessage string

type WallMessage string

func main() {
	// the quit channel
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	// read in necessary configuration
	configFile, err := iniconf.ReadConfigFile(GLCD_CONFIG)
	if err != nil {
		log.Fatal("Unable to read configuration file. Exiting now.")
	}

	glcd := &GLCD{QuitChan: sigChan}
	glcd.init(configFile)

	// receiving quit shuts down
	<-glcd.QuitChan
}

type GLCClient struct {
	Name      string
	ClientId  string
	State     *PlayerState
	Heartbeat time.Time
}

// struct type for Bot3
type GLCD struct {
	Online     bool
	ConfigFile *iniconf.ConfigFile

	// NSQ input/output
	NSQWriter             *nsq.Writer
	GLCDaemonTopic        *nsq.Reader
	GLCGameStateTopicName string
	GLCDaemonTopicChannel string
	Clients               map[string]*GLCClient

	// game state channels
	HeartbeatChan chan *Heartbeat

	QuitChan chan os.Signal

	MongoSession *mgo.Session
	MongoDB      *mgo.Database
}

func (glcd *GLCD) init(conf *iniconf.ConfigFile) error {

	glcd.ConfigFile = conf
	glcd.Online = false

	glcd.Clients = map[string]*GLCClient{}

	// Connect to Mongo.
	servers, err := glcd.ConfigFile.GetString("mongo", "servers")

	if err != nil {
		return fmt.Errorf("Mongo: No server configured.")
	}

	glcd.MongoSession, err = mgo.Dial(servers)

	if err != nil {
	}

	db, err := glcd.ConfigFile.GetString("mongo", "db")

	if err != nil {
		return fmt.Errorf("Mongo: No database configured.")
	}

	glcd.MongoDB = glcd.MongoSession.DB(db)

	// set up channels
	glcd.HeartbeatChan = make(chan *Heartbeat)

	nsqdAddress, _ := conf.GetString("nsq", "nsqd-address")
	lookupdAddress, _ := conf.GetString("nsq", "lookupd-address")
	glcd.GLCGameStateTopicName, _ = conf.GetString("nsq", "server-topic")

	glcdTopic, _ := conf.GetString("nsq", "glcd-topic")

	// Create the channel, by connecting to lookupd. (TODO; if it doesn't
	// exist. Also do it the right way with a Register command?)
	glcd.NSQWriter = nsq.NewWriter(nsqdAddress)
	glcd.NSQWriter.Publish(glcd.GLCGameStateTopicName, []byte("{\"client\":\"server\"}"))

	// set up reader for glcdTopic
	reader, err := nsq.NewReader(glcdTopic, "main")
	if err != nil {
		glcd.QuitChan <- syscall.SIGINT
	}
	glcd.GLCDaemonTopic = reader
	glcd.GLCDaemonTopic.AddHandler(glcd)
	glcd.GLCDaemonTopic.ConnectToLookupd(lookupdAddress)

	// Spawn goroutine to clear out clients who don't send hearbeats
	// anymore.
	go glcd.CleanupClients()
	go glcd.HandleHeartbeatChannel()

	return nil
}

func (glcd *GLCD) Publish(msg *Message) {
	encodedRequest, _ := json.Marshal(*msg)
	glcd.NSQWriter.Publish(glcd.GLCGameStateTopicName, encodedRequest)
}

func (glcd *GLCD) HandleHeartbeatChannel() {
	for {
		hb := <-glcd.HeartbeatChan
		fmt.Printf("HandleHeartbeatChannel: Received heartbeat: %+v\n", hb)

		// see if key and client exists in the map
		c, exists := glcd.Clients[hb.ClientId]

		if exists {
			fmt.Printf("Client %s exists.  Updating heartbeat.\n", hb.ClientId)
			c.Heartbeat = time.Now()
		} else {
			fmt.Printf("Adding client %s to client list\n", hb.ClientId)
			client := &GLCClient{ClientId: hb.ClientId, Heartbeat: time.Now()}
			glcd.Clients[hb.ClientId] = client
		}
	}
}

func (glcd *GLCD) CleanupClients() error {
	for {
		exp := time.Now().Unix()
		<-time.After(time.Second * 10)
		fmt.Println("Doing client clean up")
		// Expire any clients who haven't sent a heartbeat in the last 10 seconds.
		for k, v := range glcd.Clients {
			if v.Heartbeat.Unix() < exp {
				fmt.Printf("Deleting client %s due to inactivity.\n", v.ClientId)
				delete(glcd.Clients, k)
				//glcd.Publish(&Message{Type: "playerPassport", Data: PlayerPassport{Action: "playerGone"}}) // somehow add k to this
			} else {
				fmt.Printf("Client has not expired.")
			}
		}
	}
}

// Send a zone file update.
func (glcd *GLCD) SendZones() {
	c := glcd.MongoDB.C("zones")
	q := c.Find(nil)

	if q == nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("No zones found")})
	} else {
		var results []interface{}
		err := q.All(&results)
		if err == nil {
			for _, res := range results {
				glcd.Publish(&Message{Type: "zone", Data: res.(string)}) // dump res as a JSON string
			}
		} else {
			glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to fetch zones: %v", err)})
		}
	}
}

func (glcd *GLCD) SendZone(zone *Zone) {
	c := glcd.MongoDB.C("zones")
	query := bson.M{"zone": zone.Name}
	results := c.Find(query)

	if results == nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("No such zone '%s'", zone.Name)})
	} else {
		var res interface{}
		err := results.One(&res)
		if err == nil {
			glcd.Publish(&Message{Type: "zone", Data: res.(string)})
		} else {
			glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to fetch zone: %v", err)})
		}
	}
}

// Send a zone file update.
func (glcd *GLCD) UpdateZone(zone *Zone) {
	query := bson.M{"zone": zone.Name}
	zdata := ZoneInfo{}
	c := glcd.MongoDB.C("zones")
	val := bson.M{"type": "zone", "zdata": zdata, "timestamp": time.Now()}
	change := bson.M{"$set": val}

	err := c.Update(query, change)

	if err == mgo.ErrNotFound {
		val["id"], _ = c.Count()
		change = bson.M{"$set": val}
		err = c.Update(query, change)
	}

	if err != nil {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Unable to update zone: %v", err)})
	} else {
		glcd.Publish(&Message{Type: "error", Data: fmt.Sprintf("Updated zone '%s'", zone.Name)})
	}
}

func (glcd *GLCD) HandleMessage(nsqMessage *nsq.Message) error {

	// fmt.Println("-------")
	// fmt.Printf("Received message %s\n\n", nsqMessage.Body)
	// fmt.Println("-------")
	msg := &Message{}

	err := json.Unmarshal(nsqMessage.Body, &msg)

	if err != nil {
		fmt.Printf(err.Error())
	}

	var dataMap map[string]interface{}
	if msg.Data != nil {
		dataMap = msg.Data.(map[string]interface{})
	} else {
		dataMap = make(map[string]interface{})
	}

	if msg.Command == "playerPassport" {
		//		HandlePassport(msg.Data)
	} else if msg.Command == "playerState" {
		var ps PlayerState
		//fmt.Printf("Data is: %v, %v\n", psmap["px"], psmap["py"])
		err := ms.Decode(dataMap, &ps)
		if err != nil {
			fmt.Println(err.Error())
		}
		fmt.Printf("Player state: %+v\n", ps)
	} else if msg.Command == "zone" {
		//		HandleZoneUpdate(msg.Data)
	} else if msg.Command == "wall" {
		//		HandleWallMessage(msg.Data)
	} else if msg.Command == "heartbeat" {
		hb := &Heartbeat{}
		hb.ClientId = msg.Name
		glcd.HeartbeatChan <- hb
	} else if msg.Command == "error" {
		//		HandleError(msg.Data)
	} else {
		// log.Printf("Unknown Message Type: %s", msg.Type)
	}

	return nil
}
<|endoftext|>


<commit_before>package jujuc_test

import (
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/worker/uniter/jujuc"
	"path/filepath"
)

type ConfigGetSuite struct {
	ContextSuite
}

var _ = Suite(&ConfigGetSuite{})

var configGetYamlMap = "monsters: false\nspline-reticulation: 45\ntitle: My Title\nusername: admin001\n"

var configGetTests = []struct {
	args []string
	out  string
}{
	{[]string{"monsters"}, "false\n"},
	{[]string{"--format", "yaml", "monsters"}, "false\n"},
	{[]string{"--format", "json", "monsters"}, "false\n"},
	{[]string{"spline-reticulation"}, "45\n"},
	{[]string{"--format", "yaml", "spline-reticulation"}, "45\n"},
	{[]string{"--format", "json", "spline-reticulation"}, "45\n"},
	{[]string{"missing"}, ""},
	{[]string{"--format", "yaml", "missing"}, ""},
	{[]string{"--format", "json", "missing"}, "null\n"},
	{nil, configGetYamlMap},
	{[]string{"--format", "yaml"}, configGetYamlMap},
	{[]string{"--format", "json"}, `{"monsters":false,"spline-reticulation":45,"title":"My Title","username":"admin001"}` + "\n"},
}

func (s *ConfigGetSuite) TestOutputFormat(c *C) {
	for i, t := range configGetTests {
		c.Logf("test %d: %#v", i, t.args)
		hctx := s.GetHookContext(c, -1, "")
		com, err := jujuc.NewCommand(hctx, "config-get")
		c.Assert(err, IsNil)
		ctx := testing.Context(c)
		code := cmd.Main(com, ctx, t.args)
		c.Assert(code, Equals, 0)
		c.Assert(bufferString(ctx.Stderr), Equals, "")
		c.Assert(bufferString(ctx.Stdout), Matches, t.out)
	}
}

func (s *ConfigGetSuite) TestHelp(c *C) {
	hctx := s.GetHookContext(c, -1, "")
	com, err := jujuc.NewCommand(hctx, "config-get")
	c.Assert(err, IsNil)
	ctx := testing.Context(c)
	code := cmd.Main(com, ctx, []string{"--help"})
	c.Assert(code, Equals, 0)
	c.Assert(bufferString(ctx.Stdout), Equals, `usage: config-get [options] [<key>]
purpose: print service configuration

options:
--format  (= smart)
    specify output format (json|smart|yaml)
-o, --output (= "")
    specify an output file

If a key is given, only the value for that key will be printed.
`)
	c.Assert(bufferString(ctx.Stderr), Equals, "")
}

func (s *ConfigGetSuite) TestOutputPath(c *C) {
	hctx := s.GetHookContext(c, -1, "")
	com, err := jujuc.NewCommand(hctx, "config-get")
	c.Assert(err, IsNil)
	ctx := testing.Context(c)
	code := cmd.Main(com, ctx, []string{"--output", "some-file", "monsters"})
	c.Assert(code, Equals, 0)
	c.Assert(bufferString(ctx.Stderr), Equals, "")
	c.Assert(bufferString(ctx.Stdout), Equals, "")
	content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "some-file"))
	c.Assert(err, IsNil)
	c.Assert(string(content), Equals, "false\n")
}

func (s *ConfigGetSuite) TestUnknownArg(c *C) {
	hctx := s.GetHookContext(c, -1, "")
	com, err := jujuc.NewCommand(hctx, "config-get")
	c.Assert(err, IsNil)
	testing.TestInit(c, com, []string{"multiple", "keys"}, `unrecognized args: \["keys"\]`)
}
<commit_msg>Fix the other test that checks smart output.  My fault for not running the full test suite earlier.<commit_after>package jujuc_test

import (
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/worker/uniter/jujuc"
	"path/filepath"
)

type ConfigGetSuite struct {
	ContextSuite
}

var _ = Suite(&ConfigGetSuite{})

var configGetYamlMap = "monsters: false\nspline-reticulation: 45\ntitle: My Title\nusername: admin001\n"

var configGetTests = []struct {
	args []string
	out  string
}{
	{[]string{"monsters"}, "False\n"},
	{[]string{"--format", "yaml", "monsters"}, "false\n"},
	{[]string{"--format", "json", "monsters"}, "false\n"},
	{[]string{"spline-reticulation"}, "45\n"},
	{[]string{"--format", "yaml", "spline-reticulation"}, "45\n"},
	{[]string{"--format", "json", "spline-reticulation"}, "45\n"},
	{[]string{"missing"}, ""},
	{[]string{"--format", "yaml", "missing"}, ""},
	{[]string{"--format", "json", "missing"}, "null\n"},
	{nil, configGetYamlMap},
	{[]string{"--format", "yaml"}, configGetYamlMap},
	{[]string{"--format", "json"}, `{"monsters":false,"spline-reticulation":45,"title":"My Title","username":"admin001"}` + "\n"},
}

func (s *ConfigGetSuite) TestOutputFormat(c *C) {
	for i, t := range configGetTests {
		c.Logf("test %d: %#v", i, t.args)
		hctx := s.GetHookContext(c, -1, "")
		com, err := jujuc.NewCommand(hctx, "config-get")
		c.Assert(err, IsNil)
		ctx := testing.Context(c)
		code := cmd.Main(com, ctx, t.args)
		c.Assert(code, Equals, 0)
		c.Assert(bufferString(ctx.Stderr), Equals, "")
		c.Assert(bufferString(ctx.Stdout), Matches, t.out)
	}
}

func (s *ConfigGetSuite) TestHelp(c *C) {
	hctx := s.GetHookContext(c, -1, "")
	com, err := jujuc.NewCommand(hctx, "config-get")
	c.Assert(err, IsNil)
	ctx := testing.Context(c)
	code := cmd.Main(com, ctx, []string{"--help"})
	c.Assert(code, Equals, 0)
	c.Assert(bufferString(ctx.Stdout), Equals, `usage: config-get [options] [<key>]
purpose: print service configuration

options:
--format  (= smart)
    specify output format (json|smart|yaml)
-o, --output (= "")
    specify an output file

If a key is given, only the value for that key will be printed.
`)
	c.Assert(bufferString(ctx.Stderr), Equals, "")
}

func (s *ConfigGetSuite) TestOutputPath(c *C) {
	hctx := s.GetHookContext(c, -1, "")
	com, err := jujuc.NewCommand(hctx, "config-get")
	c.Assert(err, IsNil)
	ctx := testing.Context(c)
	code := cmd.Main(com, ctx, []string{"--output", "some-file", "monsters"})
	c.Assert(code, Equals, 0)
	c.Assert(bufferString(ctx.Stderr), Equals, "")
	c.Assert(bufferString(ctx.Stdout), Equals, "")
	content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "some-file"))
	c.Assert(err, IsNil)
	c.Assert(string(content), Equals, "False\n")
}

func (s *ConfigGetSuite) TestUnknownArg(c *C) {
	hctx := s.GetHookContext(c, -1, "")
	com, err := jujuc.NewCommand(hctx, "config-get")
	c.Assert(err, IsNil)
	testing.TestInit(c, com, []string{"multiple", "keys"}, `unrecognized args: \["keys"\]`)
}
<|endoftext|>


<commit_before>// +build go1.5

// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Gogs (Go Git Service) is a painless self-hosted Git Service.
package main

import (
	"os"
	"runtime"

	"github.com/urfave/cli"

	"github.com/gogits/gogs/cmd"
	"github.com/gogits/gogs/modules/setting"
)

const APP_VER = "0.9.115.0103"

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	setting.AppVer = APP_VER
}

func main() {
	app := cli.NewApp()
	app.Name = "Gogs"
	app.Usage = "Go Git Service: a painless self-hosted Git service"
	app.Version = APP_VER
	app.Commands = []cli.Command{
		cmd.CmdWeb,
		cmd.CmdServ,
		cmd.CmdUpdate,
		cmd.CmdDump,
		cmd.CmdCert,
		cmd.CmdAdmin,
		cmd.CmdImport,
	}
	app.Flags = append(app.Flags, []cli.Flag{}...)
	app.Run(os.Args)
}
<commit_msg>Remove call to set GOMAXPROCS (#4010)<commit_after>// +build go1.5

// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Gogs (Go Git Service) is a painless self-hosted Git Service.
package main

import (
	"os"

	"github.com/urfave/cli"

	"github.com/gogits/gogs/cmd"
	"github.com/gogits/gogs/modules/setting"
)

const APP_VER = "0.9.115.0103"

func init() {
	setting.AppVer = APP_VER
}

func main() {
	app := cli.NewApp()
	app.Name = "Gogs"
	app.Usage = "Go Git Service: a painless self-hosted Git service"
	app.Version = APP_VER
	app.Commands = []cli.Command{
		cmd.CmdWeb,
		cmd.CmdServ,
		cmd.CmdUpdate,
		cmd.CmdDump,
		cmd.CmdCert,
		cmd.CmdAdmin,
		cmd.CmdImport,
	}
	app.Flags = append(app.Flags, []cli.Flag{}...)
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package gohr

import (
	"fmt"
	"os"

	"golang.org/x/crypto/ssh/terminal"
)

//get number of columns of terminal from crypto subdirectory ssh/terminal
func getCols() int {
	c, _, err := terminal.GetSize(int(os.Stdout.Fd()))
	if err != nil {
		panic(err)
	}
	return c
}

// DrawHr fills a row with '#' by default (if no arguments are provided) or take command line arguments and print each pattern on a new line
func DrawHr(args ...string) {
	cols := getCols()

	if len(args) == 0 {
		for i := 0; i < cols; i++ {
			fmt.Printf("#")
		}
		fmt.Println()
	} else {
		for _, arg := range args {
			l := len(arg)
			for i := 0; i < cols/l; i++ {
				fmt.Printf(arg)
			}
			// Fill ups the remaining columns in the row with part of the pattern
			fmt.Printf("%v\n", arg[:cols%l])
		}
	}
}
<commit_msg>Return appropriate error from getWidth()<commit_after>package gohr

import (
	"fmt"
	"log"
	"os"

	"golang.org/x/crypto/ssh/terminal"
)

// getWidth gets number of width of terminal from crypto subdirectory ssh/terminal
func getWidth() (int, error) {
	w, _, err := terminal.GetSize(int(os.Stdout.Fd()))
	if err != nil {
		return -1, err
	}
	return w, nil
}

// Draw fills a row with '#' by default (if no arguments are provided) or takes arguments and prints each pattern on a new line.
func Draw(args ...string) {
	w, err := getWidth()
	if err != nil {
		log.Fatalf("Error getting terminal width: %s\n", err)
	}

	if len(args) == 0 {
		for i := 0; i < w; i++ {
			fmt.Printf("#")
		}
		fmt.Printf("\n")
	} else {
		for _, arg := range args {
			l := len(arg)
			for i := 0; i < w/l; i++ {
				fmt.Printf(arg)
			}
			// Fills up the remaining columns in the row with part of the pattern
			fmt.Printf("%s\n", arg[:w%l])
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/alecthomas/template"
	"github.com/toqueteos/webbrowser"
	"gopkg.in/alecthomas/kingpin.v2"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

var (
	port = kingpin.Flag("port", "Listening port used by webserver").Short('p').Default("1110").Int()
	file = kingpin.Arg("file", "Markdown file").String()
)

type EditorView struct {
	File    string
	Content string
}

func main() {
	kingpin.Version("0.0.1")
	kingpin.Parse()

	mux := http.NewServeMux()
	mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
	mux.HandleFunc("/", RootHandler)

	go WaitForServer(port)
	err := http.ListenAndServe(fmt.Sprintf(":%d", *port), mux)
	if err != nil {
		log.Fatal("Failed to listen and serve: ", err)
	}
}

func RootHandler(w http.ResponseWriter, req *http.Request) {
	log.Printf("%s %s", req.Method, req.URL)
	if req.Method == "POST" {
		req.ParseForm()
		ioutil.WriteFile(*file, []byte(req.PostForm.Get("content")), 0644)
	}
	t := template.New("base.html")
	t, err := t.ParseFiles("template/base.html")
	if err != nil {
		log.Fatalf("Unable to parse template: ", err)
	}
	w.Header().Set("Content-type", "text/html")
	content, err := ioutil.ReadFile(*file)
	if err != nil {
		log.Fatalf("Unable to open file " + *file)
	}
	ev := EditorView{File: *file, Content: string(content)}
	err = t.Execute(w, ev)
	if err != nil {
		log.Println(err)
	}
}

func WaitForServer(port *int) {
	log.Printf("Waiting for listener on port %d", *port)
	url := fmt.Sprintf("http://localhost:%d", *port)
	for {
		time.Sleep(time.Millisecond * 50)
		resp, err := http.Get(url)
		if err != nil {
			continue
		}
		resp.Body.Close()
		if resp.StatusCode != http.StatusOK {
			continue
		}
		break
	}
	log.Println("Opening " + url)
	if err := webbrowser.Open(url); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Use echo web framework<commit_after>package main

import (
	"fmt"
	"github.com/alecthomas/template"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/toqueteos/webbrowser"
	"gopkg.in/alecthomas/kingpin.v2"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

var (
	port = kingpin.Flag("port", "Listening port used by webserver").Short('p').Default("1110").Int()
	file = kingpin.Arg("file", "Markdown file").String()
)

type EditorView struct {
	File    string
	Content string
}

func main() {
	kingpin.Version("0.0.1")
	kingpin.Parse()

	e := echo.New()

	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	e.Static("/static/", "static")
	e.Get("/", RootHandler)
	e.Post("/", RootHandler)

	go WaitForServer(port)
	e.Run(fmt.Sprintf(":%d", *port))
}

func RootHandler(w http.ResponseWriter, req *http.Request) {
	if req.Method == "POST" {
		req.ParseForm()
		ioutil.WriteFile(*file, []byte(req.PostForm.Get("content")), 0644)
	}
	t := template.New("base.html")
	t, err := t.ParseFiles("template/base.html")
	if err != nil {
		log.Fatalf("Unable to parse template: ", err)
	}
	w.Header().Set("Content-type", "text/html")
	content, err := ioutil.ReadFile(*file)
	if err != nil {
		log.Fatalf("Unable to open file " + *file)
	}
	ev := EditorView{File: *file, Content: string(content)}
	err = t.Execute(w, ev)
	if err != nil {
		log.Println(err)
	}
}

func WaitForServer(port *int) {
	log.Printf("Waiting for listener on port %d", *port)
	url := fmt.Sprintf("http://localhost:%d", *port)
	for {
		time.Sleep(time.Millisecond * 50)
		resp, err := http.Get(url)
		if err != nil {
			continue
		}
		resp.Body.Close()
		if resp.StatusCode != http.StatusOK {
			continue
		}
		break
	}
	log.Println("Opening " + url)
	if err := webbrowser.Open(url); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"time"

	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/all"
)

func main() {
	embd.InitGPIO()
	defer embd.CloseGPIO()

	if pin, e := embd.NewDigitalPin(4); e != nil {
		log.Panic("NewDigitalPin ", e)
	} else {
		defer pin.Close()

		if e := pin.SetDirection(embd.Out); e != nil {
			log.Panic("SetDirection OUT ", e)
		}
		if e := pin.Write(embd.High); e != nil {
			log.Panic("DigitalWrite HIGH ", e)
		}
		time.Sleep(500 * time.Millisecond)
		if e := pin.Write(embd.Low); e != nil {
			log.Panic("DigitalWrite LOW ", e)
		}
		time.Sleep(18 * time.Millisecond)
		if e := pin.SetDirection(embd.In); e != nil {
			log.Panic("SetDirection IN ", e)
		}

		started_at := time.Now()
		for i := 0; i < 60; i++ {
			if r, e := pin.Read(); e != nil {
				log.Panic("Read ", e)
			} else {
				fmt.Printf("after %f microseconds: %d\n",
					time.Since(started_at).Seconds()*1000000, r)
			}
		}
	}

	fmt.Println("Success!")
}
<commit_msg>gpio.go: finish DHT11 temp/humidity reads<commit_after>package main

import (
	"bytes"
	"fmt"
	"log"
	"time"

	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/all"
)

func ReadDHT11(pin embd.DigitalPin) (byte, byte, error) {
	if e := pin.SetDirection(embd.Out); e != nil {
		return 0, 0, e
	}
	if e := pin.Write(embd.High); e != nil {
		return 0, 0, e
	}
	time.Sleep(500 * time.Millisecond)
	if e := pin.Write(embd.Low); e != nil {
		return 0, 0, e
	}
	time.Sleep(18 * time.Millisecond)
	if e := pin.SetDirection(embd.In); e != nil {
		return 0, 0, e
	}

	var values [5]byte

	pulses := make([]byte, (len(values)*8+1)*10)
	//started_at := time.Now()
	for i := 0; i < len(pulses); i++ {
		if pulse, e := pin.Read(); e != nil {
			return 0, 0, e
		} else {
			pulses[i] = byte(pulse)
		}
	}
	//read_time := time.Since(started_at).Seconds() * 1000000 / float64(len(pulses))

	pulse_duration := 0
	for i, j := bytes.LastIndexByte(pulses, 0), 0; i >= 0 && j < 8*len(values); i-- {
		if pulses[i] == 1 {
			pulse_duration++
		} else {
			if pulse_duration > 0 {
				value := 0
				if pulse_duration > 2 /* *read_time > 70 */ {
					value = 1
				}
				pulse_duration = 0
				values[j/8] += byte(value << byte(j%8))
				j++
			}
		}
	}

	checksum := values[4] + values[3] + values[2] + values[1]
	if checksum == values[0] {
		return values[4], values[2], nil
	}
	return 0, 0, fmt.Errorf("invalid checksum: expected %d, got %d",
		values[0], checksum)
}

func main() {
	embd.InitGPIO()
	defer embd.CloseGPIO()

	if pin, e := embd.NewDigitalPin(4); e != nil {
		log.Panic("NewDigitalPin ", e)
	} else {
		defer pin.Close()
		for {
			if h, t, e := ReadDHT11(pin); e != nil {
				fmt.Println(e)
			} else {
				fmt.Printf("%d*C, %d%%\n", t, h)
				break
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/bcicen/ctop/config"
	"github.com/bcicen/ctop/widgets"
	ui "github.com/gizak/termui"
)

type Grid struct {
	cursorID   string // id of currently selected container
	cmap       *ContainerMap
	maxRows    int
	containers Containers // sorted slice of containers
	header     *widgets.CTopHeader
}

func NewGrid() *Grid {
	cmap := NewContainerMap()
	g := &Grid{
		cmap:       cmap,
		containers: cmap.All(),
		header:     widgets.NewCTopHeader(),
	}
	return g
}

func (g *Grid) calcMaxRows() {
	g.maxRows = ui.TermHeight() - widgets.CompactHeader.Height - ui.Body.Y
}

// Set an initial cursor position, if possible
func (g *Grid) cursorReset() {
	if len(g.containers) > 0 {
		g.cursorID = g.containers[0].id
		g.containers[0].widgets.Highlight()
	}
}

// Return current cursor index
func (g *Grid) cursorIdx() int {
	for n, c := range g.containers {
		if c.id == g.cursorID {
			return n
		}
	}
	return 0
}

func (g *Grid) cursorUp() {
	idx := g.cursorIdx()
	// decrement if possible
	if idx <= 0 {
		return
	}
	active := g.containers[idx]
	next := g.containers[idx-1]

	active.widgets.UnHighlight()
	g.cursorID = next.id
	next.widgets.Highlight()

	ui.Render(ui.Body)
}

func (g *Grid) cursorDown() {
	idx := g.cursorIdx()
	// increment if possible
	if idx >= (len(g.containers) - 1) {
		return
	}
	if idx >= g.maxRows-1 {
		return
	}
	active := g.containers[idx]
	next := g.containers[idx+1]

	active.widgets.UnHighlight()
	g.cursorID = next.id
	next.widgets.Highlight()
	ui.Render(ui.Body)
}

func (g *Grid) redrawRows() {
	// reinit body rows
	g.calcMaxRows()
	ui.Body.Rows = []*ui.Row{}
	ui.Clear()

	// build layout
	if config.GetSwitchVal("enableHeader") {
		ui.Body.Y = g.header.Height()
		g.header.SetCount(len(g.containers))
		g.header.SetFilter(config.GetVal("filterStr"))
		g.header.Render()
	} else {
		ui.Body.Y = 0
	}

	ui.Body.AddRows(widgets.CompactHeader)

	var cursorVisible bool
	for n, c := range g.containers.Filter() {
		if n >= g.maxRows {
			break
		}
		if c.id == g.cursorID {
			cursorVisible = true
		}
		ui.Body.AddRows(c.widgets.Row())
	}

	if !cursorVisible {
		g.cursorReset()
	}

	ui.Body.Align()
	resizeIndicator()
	ui.Render(ui.Body)

	// dump aligned widget positions and sizes
	//for i, w := range ui.Body.Rows[1].Cols {
	//log.Infof("w%v: x=%v y=%v w=%v h=%v", i, w.X, w.Y, w.Width, w.Height)
	//}

}

// override Align()'d size for indicator column
func resizeIndicator() {
	xShift := 1
	toWidth := 3
	for _, r := range ui.Body.Rows {
		wDiff := r.Cols[0].Width - (toWidth + xShift)
		// set indicator x, width
		r.Cols[0].SetX(xShift)
		r.Cols[0].SetWidth(toWidth)

		// shift remainder of columns left by wDiff
		for _, c := range r.Cols[1:] {
			c.SetX(c.X - wDiff)
			c.SetWidth(c.Width - wDiff)
		}
	}
}

func (g *Grid) ExpandView() {
	ui.Clear()
	ui.DefaultEvtStream.ResetHandlers()
	defer ui.DefaultEvtStream.ResetHandlers()
	container, _ := g.cmap.Get(g.cursorID)
	container.Expand()
}

func logEvent(e ui.Event) {
	var s string
	s += fmt.Sprintf("Type: %s\n", e.Type)
	s += fmt.Sprintf("Path: %s\n", e.Path)
	s += fmt.Sprintf("From: %s\n", e.From)
	s += fmt.Sprintf("To: %s", e.To)
	log.Debugf("new event:\n%s", s)
}

func Display(g *Grid) bool {
	var menu func()
	var expand bool

	ui.DefaultEvtStream.Hook(logEvent)

	// initial draw
	g.redrawRows()

	ui.Handle("/sys/kbd/<up>", func(ui.Event) {
		g.cursorUp()
	})
	ui.Handle("/sys/kbd/<down>", func(ui.Event) {
		g.cursorDown()
	})
	ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
		expand = true
		ui.StopLoop()
	})

	ui.Handle("/sys/kbd/a", func(ui.Event) {
		config.Toggle("allContainers")
		g.redrawRows()
	})
	ui.Handle("/sys/kbd/f", func(ui.Event) {
		menu = FilterMenu
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/h", func(ui.Event) {
		menu = HelpMenu
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/H", func(ui.Event) {
		config.Toggle("enableHeader")
		g.redrawRows()
	})
	ui.Handle("/sys/kbd/q", func(ui.Event) {
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/r", func(e ui.Event) {
		config.Toggle("sortReversed")
	})
	ui.Handle("/sys/kbd/s", func(ui.Event) {
		menu = SortMenu
		ui.StopLoop()
	})

	ui.Handle("/timer/1s", func(e ui.Event) {
		g.containers = g.cmap.All() // refresh containers for current sort order
		g.redrawRows()
	})

	ui.Handle("/sys/wnd/resize", func(e ui.Event) {
		g.header.Align()
		ui.Body.Width = ui.TermWidth()
		log.Infof("resize: width=%v max-rows=%v", ui.Body.Width, g.maxRows)
		g.redrawRows()
	})

	ui.Loop()
	if menu != nil {
		menu()
		return false
	}
	if expand {
		g.ExpandView()
		return false
	}
	return true
}
<commit_msg>move maxrows method out of grid<commit_after>package main

import (
	"fmt"

	"github.com/bcicen/ctop/config"
	"github.com/bcicen/ctop/widgets"
	ui "github.com/gizak/termui"
)

func maxRows() int {
	return ui.TermHeight() - widgets.CompactHeader.Height - ui.Body.Y
}

type Grid struct {
	cursorID   string // id of currently selected container
	cmap       *ContainerMap
	containers Containers // sorted slice of containers
	header     *widgets.CTopHeader
}

func NewGrid() *Grid {
	cmap := NewContainerMap()
	g := &Grid{
		cmap:       cmap,
		containers: cmap.All(),
		header:     widgets.NewCTopHeader(),
	}
	return g
}

// Set an initial cursor position, if possible
func (g *Grid) cursorReset() {
	if len(g.containers) > 0 {
		g.cursorID = g.containers[0].id
		g.containers[0].widgets.Highlight()
	}
}

// Return current cursor index
func (g *Grid) cursorIdx() int {
	for n, c := range g.containers {
		if c.id == g.cursorID {
			return n
		}
	}
	return 0
}

func (g *Grid) cursorUp() {
	idx := g.cursorIdx()
	// decrement if possible
	if idx <= 0 {
		return
	}
	active := g.containers[idx]
	next := g.containers[idx-1]

	active.widgets.UnHighlight()
	g.cursorID = next.id
	next.widgets.Highlight()

	ui.Render(ui.Body)
}

func (g *Grid) cursorDown() {
	idx := g.cursorIdx()
	// increment if possible
	if idx >= (len(g.containers) - 1) {
		return
	}
	if idx >= maxRows()-1 {
		return
	}
	active := g.containers[idx]
	next := g.containers[idx+1]

	active.widgets.UnHighlight()
	g.cursorID = next.id
	next.widgets.Highlight()
	ui.Render(ui.Body)
}

func (g *Grid) redrawRows() {
	// reinit body rows
	ui.Body.Rows = []*ui.Row{}
	ui.Clear()

	// build layout
	if config.GetSwitchVal("enableHeader") {
		ui.Body.Y = g.header.Height()
		g.header.SetCount(len(g.containers))
		g.header.SetFilter(config.GetVal("filterStr"))
		g.header.Render()
	} else {
		ui.Body.Y = 0
	}

	ui.Body.AddRows(widgets.CompactHeader)

	var cursorVisible bool
	max := maxRows()
	for n, c := range g.containers.Filter() {
		if n >= max {
			break
		}
		if c.id == g.cursorID {
			cursorVisible = true
		}
		ui.Body.AddRows(c.widgets.Row())
	}

	if !cursorVisible {
		g.cursorReset()
	}

	ui.Body.Align()
	resizeIndicator()
	ui.Render(ui.Body)

	// dump aligned widget positions and sizes
	//for i, w := range ui.Body.Rows[1].Cols {
	//log.Infof("w%v: x=%v y=%v w=%v h=%v", i, w.X, w.Y, w.Width, w.Height)
	//}

}

// override Align()'d size for indicator column
func resizeIndicator() {
	xShift := 1
	toWidth := 3
	for _, r := range ui.Body.Rows {
		wDiff := r.Cols[0].Width - (toWidth + xShift)
		// set indicator x, width
		r.Cols[0].SetX(xShift)
		r.Cols[0].SetWidth(toWidth)

		// shift remainder of columns left by wDiff
		for _, c := range r.Cols[1:] {
			c.SetX(c.X - wDiff)
			c.SetWidth(c.Width - wDiff)
		}
	}
}

func (g *Grid) ExpandView() {
	ui.Clear()
	ui.DefaultEvtStream.ResetHandlers()
	defer ui.DefaultEvtStream.ResetHandlers()
	container, _ := g.cmap.Get(g.cursorID)
	container.Expand()
}

func logEvent(e ui.Event) {
	var s string
	s += fmt.Sprintf("Type: %s\n", e.Type)
	s += fmt.Sprintf("Path: %s\n", e.Path)
	s += fmt.Sprintf("From: %s\n", e.From)
	s += fmt.Sprintf("To: %s", e.To)
	log.Debugf("new event:\n%s", s)
}

func Display(g *Grid) bool {
	var menu func()
	var expand bool

	ui.DefaultEvtStream.Hook(logEvent)

	// initial draw
	g.redrawRows()

	ui.Handle("/sys/kbd/<up>", func(ui.Event) {
		g.cursorUp()
	})
	ui.Handle("/sys/kbd/<down>", func(ui.Event) {
		g.cursorDown()
	})
	ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
		expand = true
		ui.StopLoop()
	})

	ui.Handle("/sys/kbd/a", func(ui.Event) {
		config.Toggle("allContainers")
		g.redrawRows()
	})
	ui.Handle("/sys/kbd/f", func(ui.Event) {
		menu = FilterMenu
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/h", func(ui.Event) {
		menu = HelpMenu
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/H", func(ui.Event) {
		config.Toggle("enableHeader")
		g.redrawRows()
	})
	ui.Handle("/sys/kbd/q", func(ui.Event) {
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/r", func(e ui.Event) {
		config.Toggle("sortReversed")
	})
	ui.Handle("/sys/kbd/s", func(ui.Event) {
		menu = SortMenu
		ui.StopLoop()
	})

	ui.Handle("/timer/1s", func(e ui.Event) {
		g.containers = g.cmap.All() // refresh containers for current sort order
		g.redrawRows()
	})

	ui.Handle("/sys/wnd/resize", func(e ui.Event) {
		g.header.Align()
		ui.Body.Width = ui.TermWidth()
		log.Infof("resize: width=%v max-rows=%v", ui.Body.Width, maxRows())
		g.redrawRows()
	})

	ui.Loop()
	if menu != nil {
		menu()
		return false
	}
	if expand {
		g.ExpandView()
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package main

import "math"
import "math/rand"
import "strings"
import "errors"
import "strconv"
import "fmt"

type Grid struct {
	Dimensions []Dimension
	GuessCount int
}

type Dimension struct {
	TargetValue   int
	Maximum       int
	Minimum       int
	ErrorMessage  string
	DimensionName string
	LowHint       string
	HighHint      string
	Found         bool
}

func (gc Dimension) String() string {
	return fmt.Sprintf("This is the %v", gc.DimensionName)
}

func randInt(min int, max int) int {
	return min + rand.Intn(max-min)
}

func (g *Grid) ProcessGuess(raw_guess string) (GuessResult, error) {
	result := GuessResult{}
	g.GuessCount += 1
	valid_len := 3
	err_msg := "Guess must be in the format #,#,#.  Example:  5,5,5"

	parts := strings.Split(raw_guess, ",")
	if len(parts) != valid_len {
		err := errors.New(err_msg)
		return result, err
	}
	guess_coordinates := make([]int, 3)

	for index, g := range parts {
		guess_val := 0
		err_d := errors.New("")
		guess_val, err_d = strconv.Atoi(g)
		if err_d != nil {
			err := errors.New(err_msg)
			return result, err
		}
		guess_coordinates[index] = guess_val
	}
	temp := ""
	result.Found = true
	for index, dimension := range g.Dimensions {
		if guess_coordinates[index] < dimension.TargetValue {
			temp += " - " + dimension.LowHint
			result.Found = false
		}
		if guess_coordinates[index] > dimension.TargetValue {
			temp += " - " + dimension.HighHint
			result.Found = false
		}
		if guess_coordinates[index] == dimension.TargetValue {
			temp += " - " + dimension.DimensionName + " is correct."
		}
	}
	if result.Found {
		result.Message = "You guessed the point!"
	} else {
		result.Message = temp
	}

	return result, nil
}

func (g *Grid) Build(length int) {
	low:=0
	high:=0
	if math.Mod(float64(length), 2) == 0 {
		low = length / 2 * -1
		high = length / 2
	} else {
		low = (length-1) / 2 * -1
		high = (length+1) /2
	}

	fmt.Printf("Max West is : %v.  Max East is %v.\n",low,high)
	fmt.Printf("Max South is :%v.  Max North is %v.\n",low,high)
	fmt.Printf("Furthest is :%v.  Closest is %v.\n",low,high)

	x := MakeGridDimension(low, high, "East", "West", "X Axis")
	y := MakeGridDimension(low, high, "North", "South", "Y Axis")
	z := MakeGridDimension(low, high, "Closer", "Further", "Z Axis")
	g.Dimensions = append(g.Dimensions, x, y, z)
	g.GuessCount = 1
}

func MakeGridDimension(min int, max int, lowhint string, highhint string, dimensionname string) Dimension {
	result := Dimension{}
	result.Minimum = min
	result.Maximum = max
	result.LowHint = lowhint
	result.HighHint = highhint
	result.DimensionName = dimensionname
	result.TargetValue = randInt(min, max)
	result.Found = false
	return result
}
<commit_msg>Setting description field<commit_after>package main

import "math"
import "math/rand"
import "strings"
import "errors"
import "strconv"
import "fmt"

type Grid struct {
	Dimensions []Dimension
	GuessCount int
}

type Dimension struct {
	TargetValue   int
	Maximum       int
	Minimum       int
	Description   string
	DimensionName string
	LowHint       string
	HighHint      string
	Found         bool
}

func (gc Dimension) String() string {
	return fmt.Sprintf("This is the %v", gc.DimensionName)
}

func randInt(min int, max int) int {
	return min + rand.Intn(max-min)
}

func (g *Grid) ProcessGuess(raw_guess string) (GuessResult, error) {
	result := GuessResult{}
	g.GuessCount += 1
	valid_len := 3
	err_msg := "Guess must be in the format #,#,#.  Example:  5,5,5"

	parts := strings.Split(raw_guess, ",")
	if len(parts) != valid_len {
		err := errors.New(err_msg)
		return result, err
	}
	guess_coordinates := make([]int, 3)

	for index, g := range parts {
		guess_val := 0
		err_d := errors.New("")
		guess_val, err_d = strconv.Atoi(g)
		if err_d != nil {
			err := errors.New(err_msg)
			return result, err
		}
		guess_coordinates[index] = guess_val
	}
	temp := ""
	result.Found = true
	for index, dimension := range g.Dimensions {
		if guess_coordinates[index] < dimension.TargetValue {
			temp += " - " + dimension.LowHint
			result.Found = false
		}
		if guess_coordinates[index] > dimension.TargetValue {
			temp += " - " + dimension.HighHint
			result.Found = false
		}
		if guess_coordinates[index] == dimension.TargetValue {
			temp += " - " + dimension.DimensionName + " is correct."
		}
	}
	if result.Found {
		result.Message = "You guessed the point!"
	} else {
		result.Message = temp
	}

	return result, nil
}

func (g *Grid) Build(length int) {
	low := 0
	high := 0
	if math.Mod(float64(length), 2) == 0 {
		low = length / 2 * -1
		high = length / 2
	} else {
		low = (length - 1) / 2 * -1
		high = (length + 1) / 2
	}

	x_desc = fmt.Scanf(" Max West: %v.   Max East: %v.\n", low, high)
	y_desc = fmt.Scanf("Max South:%v.   Max North: %v.\n", low, high)
	z_desc = fmt.Scanf(" Furthest:%v.     Closest: %v.\n", low, high)

	x := MakeGridDimension(low, high, "East", "West", "X Axis", x_desc)
	y := MakeGridDimension(low, high, "North", "South", "Y Axis", y_desc)
	z := MakeGridDimension(low, high, "Closer", "Further", "Z Axis", z_desc)
	g.Dimensions = append(g.Dimensions, x, y, z)
	g.GuessCount = 1
}

func MakeGridDimension(min int, max int, lowhint string, highhint string, dimensionname string, description string) Dimension {
	result := Dimension{}
	result.Minimum = min
	result.Maximum = max
	result.LowHint = lowhint
	result.HighHint = highhint
	result.DimensionName = dimensionname
	result.TargetValue = randInt(min, max)
	result.Found = false
	return result
}
<|endoftext|>


<commit_before>package dokugen

const DIM = 9

type Cell struct {
	Number int
	Row    int
	Col    int
}

type CellList interface {
	All() chan *Cell
	Without(cell *Cell) chan *Cell
}

type Grid struct {
	data  string
	cells *[DIM * DIM]Cell
}
<commit_msg>Some more work. No new tests.<commit_after>package dokugen

const DIM = 9

type Cell struct {
	grid   *Grid
	Number int
	Row    int
	Col    int
	simpleCellList
}

type CellList interface {
	All() chan *Cell
	Without(cell *Cell) chan *Cell
}

type simpleCellList struct {
	grid   *Grid
	start  int
	end    int
	stride int
	cache  []*Cell
}

type Grid struct {
	data  string
	cells *[DIM * DIM]Cell
}

func (self *simpleCellList) All() chan *Cell {
	result := make(chan *Cell, DIM*DIM)
	if self.cache == nil {
		self.buildCache()
	}
	go func() {
		for _, cell := range self.cache {
			result <- cell
		}
	}()
	return result
}

func (self *simpleCellList) buildCache() {
	i := self.start
	if self.stride == 0 {
		self.stride = 1
	}
	for i < self.end {
		self.cache = append(self.cache, &self.grid.cells[i])
		i = i + self.stride
	}
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
// Copyright (c) 2014 Claes Mogren
// http://opensource.org/licenses/MIT

// Code to generate a weighted random identity based on weighted input files
// http://www.keithschwarz.com/darts-dice-coins/
// http://web.eecs.utk.edu/~vose/Publications/random.pdf
package main

import (
	"./vose/"
	"bufio"
	"encoding/json"
	"encoding/xml"
	"flag"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"time"
)

var langFlag string
var listLangFlag bool
var verbose bool
var nrLoopsFlag int
var jsonFlag bool
var xmlFlag bool

// Rig holds a randomly generated identity
type Rig struct {
	Firstname    string `json:"firstname" xml:"firstname"`
	Lastname     string `json:"lastname" xml:"lastname"`
	Street       string `json:"street" xml:"street"`
	Streetnumber int    `json:"nr" xml:"nr"`
	Zipcode      int    `json:"zip" xml:"zip"`
	City         string `json:"city" xml:"city"`
}

// AsText prints the Rig as text
func (r Rig) AsText() string {
	str := fmt.Sprintln(r.Firstname, r.Lastname)
	if langFlag == "en_us" {
		str += fmt.Sprintln(r.Streetnumber, r.Street)
	} else {
		str += fmt.Sprintln(r.Street, r.Streetnumber)
	}
	str += fmt.Sprintln(r.Zipcode, r.City)
	return str
}

// AsJSON will output the Rig as JSON
func (r Rig) AsJSON() string {
	b, err := json.MarshalIndent(r, "", "  ")
	if err != nil {
		fmt.Println("error:", err)
	}
	return string(b)
}

// AsXML will output the Rig as XML
func (r Rig) AsXML() string {
	b, err := xml.MarshalIndent(r, "", "  ")
	if err != nil {
		fmt.Println("error:", err)
	}
	return string(b)
}

// RigFile contains all the randomly generated identities
type RigFile struct {
	tot   float64
	texts [][]string
	vose  *vose.Vose
}

// RigDict is the dictionary for the given language
type RigDict struct {
	fnames, mnames, lnames, streets, zipcodes RigFile
}

func init() {
	flag.StringVar(&langFlag, "lang", "en_us", "Select ISO 639-1 language code, defaults to USA")
	flag.BoolVar(&listLangFlag, "l", false, "List available ISO language codes")
	flag.BoolVar(&verbose, "v", false, "Verbose output")
	flag.BoolVar(&jsonFlag, "j", false, "Print as JSON")
	flag.BoolVar(&xmlFlag, "x", false, "Print as XML")
	flag.IntVar(&nrLoopsFlag, "n", 1, "Number of identities to output")
}

func main() {
	flag.Parse()
	if listLangFlag {
		listLangs()
		os.Exit(0)
	}
	rand.Seed(time.Now().UnixNano())
	dict := loadData(langFlag)
	for i := 0; i < nrLoopsFlag; i++ {
		rig := getNext(dict)
		if jsonFlag {
			fmt.Println(rig.AsJSON())
		} else if xmlFlag {
			fmt.Println(rig.AsXML())
		} else {
			fmt.Println(rig.AsText())
		}
	}
}

func listLangs() {
	// for all dirs in data
	files, _ := ioutil.ReadDir("./data/")
	for _, f := range files {
		if f.IsDir() && !strings.HasPrefix(f.Name(), ",") {
			if validateDir(f.Name()) {
				fmt.Println(f.Name())
			}
		}
	}
}

func validateDir(iso string) bool {
	// Check for fnames, mnames, lnames, zipcodes and Streets
	srcFileNames := []string{"fnames.grig", "lnames.grig", "mnames.grig", "Streets.grig", "zipcodes.grig"}
	valid := true
	filename := ""
	for _, srcFile := range srcFileNames {
		filename = "./data/" + iso + "/" + srcFile
		if _, err := os.Stat(filename); os.IsNotExist(err) {
			if verbose {
				fmt.Println("Data file", srcFile, "missing for", iso)
			}
			valid = false
		}
	}
	return valid
}

func loadData(iso string) RigDict {
	dict := RigDict{}
	dict.fnames = loadFile(iso, "fnames.grig")
	dict.mnames = loadFile(iso, "mnames.grig")
	dict.lnames = loadFile(iso, "lnames.grig")
	dict.streets = loadFile(iso, "Streets.grig")
	dict.zipcodes = loadFile(iso, "zipcodes.grig")
	if verbose {
		fmt.Println("fname tot:", dict.fnames.tot, dict.fnames.texts[0])
		fmt.Println("mname tot:", dict.mnames.tot, dict.mnames.texts[0])
		fmt.Println("lname tot:", dict.lnames.tot, dict.lnames.texts[0])
	}
	return dict
}

func loadFile(iso string, srcFile string) RigFile {
	file, err := os.Open("data/" + iso + "/" + srcFile)
	rigFile := RigFile{}
	rigFile.texts = make([][]string, 0)
	defer file.Close()
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}
	scanner := bufio.NewScanner(file)
	sum := 0.0
	var weights []float64
	var dataStr, str []string
	for scanner.Scan() {
		scanText := scanner.Text()
		if strings.HasPrefix(scanText, "#") {
			continue
		}
		dataStr = strings.Split(scanner.Text(), "\t")
		// string to float
		f, err := strconv.ParseFloat(dataStr[0], 64)
		if err != nil {
			// Ignore error
			fmt.Println(err)
		}
		sum += f
		str = dataStr[1:]
		weights = append(weights, f)
		rigFile.texts = append(rigFile.texts, str)
		if verbose {
			fmt.Println("P: ", f, str)
		}
	}
	rigFile.tot = sum
	rigFile.vose, err = vose.NewVose(weights, rand.New(rand.NewSource(time.Now().UnixNano())))
	if err != nil {
		fmt.Println("Vose:", err)
	}
	return rigFile
}

func getNext(dict RigDict) Rig {
	rig := Rig{}
	if rand.Intn(2) == 0 {
		rig.Firstname = dict.fnames.texts[dict.fnames.vose.Next()][0]
	} else {
		rig.Firstname = dict.mnames.texts[dict.mnames.vose.Next()][0]
	}
	rig.Lastname = dict.lnames.texts[dict.lnames.vose.Next()][0]
	rig.Street = dict.streets.texts[dict.streets.vose.Next()][0]
	rig.Streetnumber = rand.Intn(59) + 1
	zip := dict.zipcodes.texts[dict.zipcodes.vose.Next()]
	rig.City = zip[1]
	rig.Zipcode, _ = strconv.Atoi(zip[0])
	return rig
}
<commit_msg>Return a proper json list<commit_after>// The MIT License (MIT)
// Copyright (c) 2014 Claes Mogren
// http://opensource.org/licenses/MIT

// Code to generate a weighted random identity based on weighted input files
// http://www.keithschwarz.com/darts-dice-coins/
// http://web.eecs.utk.edu/~vose/Publications/random.pdf
package main

import (
	"./vose/"
	"bufio"
	"encoding/json"
	"encoding/xml"
	"flag"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"time"
)

var langFlag string
var listLangFlag bool
var verbose bool
var nrLoopsFlag int
var jsonFlag bool
var xmlFlag bool

// Rig holds a randomly generated identity
type Rig struct {
	Firstname    string `json:"firstname" xml:"firstname"`
	Lastname     string `json:"lastname" xml:"lastname"`
	Street       string `json:"street" xml:"street"`
	Streetnumber int    `json:"nr" xml:"nr"`
	Zipcode      int    `json:"zip" xml:"zip"`
	City         string `json:"city" xml:"city"`
}

// AsText prints the Rig as text
func (r Rig) AsText() string {
	str := fmt.Sprintln(r.Firstname, r.Lastname)
	if langFlag == "en_us" {
		str += fmt.Sprintln(r.Streetnumber, r.Street)
	} else {
		str += fmt.Sprintln(r.Street, r.Streetnumber)
	}
	str += fmt.Sprintln(r.Zipcode, r.City)
	return str
}

// AsJSON will output the Rig as JSON
func (r Rig) AsJSON(isLast bool) string {
	b, err := json.MarshalIndent(r, "", "  ")
	if err != nil {
		fmt.Println("error:", err)
	}
	if isLast {
		return string(b)
	} else {
		return string(b) + ","
	}
}

// AsXML will output the Rig as XML
func (r Rig) AsXML() string {
	b, err := xml.MarshalIndent(r, "", "  ")
	if err != nil {
		fmt.Println("error:", err)
	}
	return string(b)
}

// RigFile contains all the randomly generated identities
type RigFile struct {
	tot   float64
	texts [][]string
	vose  *vose.Vose
}

// RigDict is the dictionary for the given language
type RigDict struct {
	fnames, mnames, lnames, streets, zipcodes RigFile
}

func init() {
	flag.StringVar(&langFlag, "lang", "en_us", "Select ISO 639-1 language code, defaults to USA")
	flag.BoolVar(&listLangFlag, "l", false, "List available ISO language codes")
	flag.BoolVar(&verbose, "v", false, "Verbose output")
	flag.BoolVar(&jsonFlag, "j", false, "Print as JSON")
	flag.BoolVar(&xmlFlag, "x", false, "Print as XML")
	flag.IntVar(&nrLoopsFlag, "n", 1, "Number of identities to output")
}

func main() {
	flag.Parse()
	if listLangFlag {
		listLangs()
		os.Exit(0)
	}
	rand.Seed(time.Now().UnixNano())
	dict := loadData(langFlag)
	if jsonFlag && nrLoopsFlag > 1 {
		fmt.Println("\"list\": [")
	}
	for i := 0; i < nrLoopsFlag; i++ {
		rig := getNext(dict)
		if jsonFlag {
			fmt.Println(rig.AsJSON(i == (nrLoopsFlag - 1)))
		} else if xmlFlag {
			fmt.Println(rig.AsXML())
		} else {
			fmt.Println(rig.AsText())
		}
	}
	if jsonFlag && nrLoopsFlag > 1 {
		fmt.Println("]")
	}
}

func listLangs() {
	// for all dirs in data
	files, _ := ioutil.ReadDir("./data/")
	for _, f := range files {
		if f.IsDir() && !strings.HasPrefix(f.Name(), ",") {
			if validateDir(f.Name()) {
				fmt.Println(f.Name())
			}
		}
	}
}

func validateDir(iso string) bool {
	// Check for fnames, mnames, lnames, zipcodes and Streets
	srcFileNames := []string{"fnames.grig", "lnames.grig", "mnames.grig", "Streets.grig", "zipcodes.grig"}
	valid := true
	filename := ""
	for _, srcFile := range srcFileNames {
		filename = "./data/" + iso + "/" + srcFile
		if _, err := os.Stat(filename); os.IsNotExist(err) {
			if verbose {
				fmt.Println("Data file", srcFile, "missing for", iso)
			}
			valid = false
		}
	}
	return valid
}

func loadData(iso string) RigDict {
	dict := RigDict{}
	dict.fnames = loadFile(iso, "fnames.grig")
	dict.mnames = loadFile(iso, "mnames.grig")
	dict.lnames = loadFile(iso, "lnames.grig")
	dict.streets = loadFile(iso, "Streets.grig")
	dict.zipcodes = loadFile(iso, "zipcodes.grig")
	if verbose {
		fmt.Println("fname tot:", dict.fnames.tot, dict.fnames.texts[0])
		fmt.Println("mname tot:", dict.mnames.tot, dict.mnames.texts[0])
		fmt.Println("lname tot:", dict.lnames.tot, dict.lnames.texts[0])
	}
	return dict
}

func loadFile(iso string, srcFile string) RigFile {
	file, err := os.Open("data/" + iso + "/" + srcFile)
	rigFile := RigFile{}
	rigFile.texts = make([][]string, 0)
	defer file.Close()
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}
	scanner := bufio.NewScanner(file)
	sum := 0.0
	var weights []float64
	var dataStr, str []string
	for scanner.Scan() {
		scanText := scanner.Text()
		if strings.HasPrefix(scanText, "#") {
			continue
		}
		dataStr = strings.Split(scanner.Text(), "\t")
		// string to float
		f, err := strconv.ParseFloat(dataStr[0], 64)
		if err != nil {
			// Ignore error
			fmt.Println(err)
		}
		sum += f
		str = dataStr[1:]
		weights = append(weights, f)
		rigFile.texts = append(rigFile.texts, str)
		if verbose {
			fmt.Println("P: ", f, str)
		}
	}
	rigFile.tot = sum
	rigFile.vose, err = vose.NewVose(weights, rand.New(rand.NewSource(time.Now().UnixNano())))
	if err != nil {
		fmt.Println("Vose:", err)
	}
	return rigFile
}

func getNext(dict RigDict) Rig {
	rig := Rig{}
	if rand.Intn(2) == 0 {
		rig.Firstname = dict.fnames.texts[dict.fnames.vose.Next()][0]
	} else {
		rig.Firstname = dict.mnames.texts[dict.mnames.vose.Next()][0]
	}
	rig.Lastname = dict.lnames.texts[dict.lnames.vose.Next()][0]
	rig.Street = dict.streets.texts[dict.streets.vose.Next()][0]
	rig.Streetnumber = rand.Intn(59) + 1
	zip := dict.zipcodes.texts[dict.zipcodes.vose.Next()]
	rig.City = zip[1]
	rig.Zipcode, _ = strconv.Atoi(zip[0])
	return rig
}
<|endoftext|>


<commit_before>package grim

// Copyright 2015 MediaMath <http://www.mediamath.com>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import "fmt"

// Instance models the state of a configured Grim instance.
type Instance struct {
	configRoot *string
	queue      *sqsQueue
}

// SetConfigRoot sets the base path of the configuration directory and clears any previously read config values from memory.
func (i *Instance) SetConfigRoot(path string) {
	i.configRoot = &path
	i.queue = nil
}

// PrepareGrimQueue creates or reuses the Amazon SQS queue named in the config.
func (i *Instance) PrepareGrimQueue() error {
	configRoot := getEffectiveConfigRoot(i.configRoot)

	config, err := getEffectiveGlobalConfig(configRoot)
	if err != nil {
		return fatalGrimErrorf("error while reading config: %v", err)
	}

	queue, err := prepareSQSQueue(config.awsKey, config.awsSecret, config.awsRegion, config.grimQueueName)
	if err != nil {
		return fatalGrimErrorf("error preparing queue: %v", err)
	}

	i.queue = queue

	return nil
}

// PrepareRepos discovers all repos that are configured then sets up SNS and GitHub.
// It is an error to call this without calling PrepareGrimQueue first.
func (i *Instance) PrepareRepos() error {
	if err := i.checkGrimQueue(); err != nil {
		return err
	}

	configRoot := getEffectiveConfigRoot(i.configRoot)

	config, err := getEffectiveGlobalConfig(configRoot)
	if err != nil {
		return fatalGrimErrorf("error while reading config: %v", err)
	}

	repos := getAllConfiguredRepos(configRoot)

	var topicARNs []string
	for _, repo := range repos {
		localConfig, err := getEffectiveConfig(configRoot, repo.owner, repo.name)
		if err != nil {
			return fatalGrimErrorf("error while reading local config: %v", err)
		}

		snsTopicName := fmt.Sprintf("grim-%v-%v-repo-topic", repo.owner, repo.name)

		snsTopicARN, err := prepareSNSTopic(config.awsKey, config.awsSecret, config.awsRegion, snsTopicName)
		if err != nil {
			return fatalGrimErrorf("error creating SNS topic: %v", err)
		}

		err = prepareSubscription(config.awsKey, config.awsSecret, config.awsRegion, snsTopicARN, i.queue.ARN)
		if err != nil {
			return fatalGrimErrorf("error subscribing Grim queue %q to SNS topic %q: %v", i.queue.ARN, snsTopicARN, err)
		}
		err = prepareAmazonSNSService(localConfig.gitHubToken, repo.owner, repo.name, snsTopicARN, config.awsKey, config.awsSecret, config.awsRegion)
		if err != nil {
			return fatalGrimErrorf("error creating configuring GitHub AmazonSNS service: %v", err)
		}
		topicARNs = append(topicARNs, snsTopicARN)
	}

	err = setPolicy(config.awsKey, config.awsSecret, config.awsRegion, i.queue.ARN, i.queue.URL, topicARNs)
	if err != nil {
		return fatalGrimErrorf("error setting policy for Grim queue %q with topics %v: %v", i.queue.ARN, topicARNs, err)
	}

	return nil
}

// BuildNextInGrimQueue creates or reuses an SQS queue as a source of work.
func (i *Instance) BuildNextInGrimQueue() error {
	if err := i.checkGrimQueue(); err != nil {
		return err
	}

	configRoot := getEffectiveConfigRoot(i.configRoot)

	globalConfig, err := getEffectiveGlobalConfig(configRoot)
	if err != nil {
		return grimErrorf("error while reading config: %v", err)
	}

	message, err := getNextMessage(globalConfig.awsKey, globalConfig.awsSecret, globalConfig.awsRegion, i.queue.URL)
	if err != nil {
		return grimErrorf("error retrieving message from Grim queue %q: %v", i.queue.URL, err)
	}

	if message != "" {
		hook, err := extractHookEvent(message)
		if err != nil {
			return grimErrorf("error extracting hook from message: %v", err)
		}

		if !(hook.eventName == "push" || hook.eventName == "pull_request" && (hook.action == "opened" || hook.action == "reopened" || hook.action == "synchronize")) {
			return nil
		}

		if hook.eventName == "pull_request" {
			sha, err := pollForMergeCommitSha(globalConfig.gitHubToken, hook.owner, hook.repo, hook.prNumber)
			if err != nil {
				return grimErrorf("error getting merge commit sha: %v", err)
			} else if sha == "" {
				return grimErrorf("error getting merge commit sha: field empty")
			}
			hook.ref = sha
		}

		localConfig, err := getEffectiveConfig(configRoot, hook.owner, hook.repo)
		if err != nil {
			return grimErrorf("error while reading config: %v", err)
		}

		return buildForHook(configRoot, localConfig, *hook)
	}

	return nil
}

// BuildRef builds a git ref immediately.
func (i *Instance) BuildRef(owner, repo, ref string) error {
	configRoot := getEffectiveConfigRoot(i.configRoot)

	config, err := getEffectiveConfig(configRoot, owner, repo)
	if err != nil {
		return fatalGrimErrorf("error while reading config: %v", err)
	}

	return buildForHook(configRoot, config, hookEvent{
		owner: owner,
		repo:  repo,
		ref:   ref,
	})
}

func buildForHook(configRoot string, config *effectiveConfig, hook hookEvent) error {
	extraEnv := hook.env()

	// TODO: do something with the err
	notifyPending(config, hook)

	result, err := build(configRoot, config.workspaceRoot, config.pathToCloneIn, hook.owner, hook.repo, extraEnv)
	if err != nil {
		notifyError(config, hook)
		return fatalGrimErrorf("error during %v: %v", describeHook(hook), err)
	}

	var notifyError error
	if result.ExitCode == 0 {
		notifyError = notifySuccess(config, hook)
	} else {
		notifyError = notifyFailure(config, hook)
	}

	err = appendResult(config.resultRoot, hook.owner, hook.repo, *result)
	if err != nil {
		return fatalGrimErrorf("error while storing result: %v", err)
	}

	return notifyError
}

func describeHook(hook hookEvent) string {
	return fmt.Sprintf("build of %v/%v initiated by a %q to %q by %q", hook.owner, hook.repo, hook.eventName, hook.target, hook.userName)
}

func notifyPending(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSPending, fmt.Sprintf("Starting %v", describeHook(hook)), ColorYellow)
}

func notifyError(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSError, fmt.Sprintf("Error during %v", describeHook(hook)), ColorGray)
}

func notifyFailure(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSFailure, fmt.Sprintf("Failure during %v", describeHook(hook)), ColorRed)
}

func notifySuccess(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSSuccess, fmt.Sprintf("Success after %v", describeHook(hook)), ColorGreen)
}

func notify(config *effectiveConfig, hook hookEvent, state refStatus, message string, color messageColor) error {
	if hook.eventName != "push" && hook.eventName != "pull_request" {
		return nil
	}

	//add grimServerID/grimQueueName to hipchat message
	message += "ServerID:" + config.grimServerID
	fmt.Printf("The message is:", message)

	ghErr := setRefStatus(config.gitHubToken, hook.owner, hook.repo, hook.statusRef, state, "", message)

	if config.hipChatToken != "" && config.hipChatRoom != "" {
		err := sendMessageToRoom(config.hipChatToken, config.hipChatRoom, "Grim", message, color)
		if err != nil {
			return err
		}
	}

	return ghErr
}

func (i *Instance) checkGrimQueue() error {
	if i.queue == nil {
		return fatalGrimErrorf("the Grim queue must be prepared first")
	}

	return nil
}
<commit_msg>grim local test<commit_after>package grim

// Copyright 2015 MediaMath <http://www.mediamath.com>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import "fmt"

// Instance models the state of a configured Grim instance.
type Instance struct {
	configRoot *string
	queue      *sqsQueue
}

// SetConfigRoot sets the base path of the configuration directory and clears any previously read config values from memory.
func (i *Instance) SetConfigRoot(path string) {
	i.configRoot = &path
	i.queue = nil
}

// PrepareGrimQueue creates or reuses the Amazon SQS queue named in the config.
func (i *Instance) PrepareGrimQueue() error {
	configRoot := getEffectiveConfigRoot(i.configRoot)

	config, err := getEffectiveGlobalConfig(configRoot)
	if err != nil {
		return fatalGrimErrorf("error while reading config: %v", err)
	}

	queue, err := prepareSQSQueue(config.awsKey, config.awsSecret, config.awsRegion, config.grimQueueName)
	if err != nil {
		return fatalGrimErrorf("error preparing queue: %v", err)
	}

	i.queue = queue

	return nil
}

// PrepareRepos discovers all repos that are configured then sets up SNS and GitHub.
// It is an error to call this without calling PrepareGrimQueue first.
func (i *Instance) PrepareRepos() error {
	if err := i.checkGrimQueue(); err != nil {
		return err
	}

	configRoot := getEffectiveConfigRoot(i.configRoot)

	config, err := getEffectiveGlobalConfig(configRoot)
	if err != nil {
		return fatalGrimErrorf("error while reading config: %v", err)
	}

	repos := getAllConfiguredRepos(configRoot)

	var topicARNs []string
	for _, repo := range repos {
		localConfig, err := getEffectiveConfig(configRoot, repo.owner, repo.name)
		if err != nil {
			return fatalGrimErrorf("error while reading local config: %v", err)
		}

		snsTopicName := fmt.Sprintf("grim-%v-%v-repo-topic", repo.owner, repo.name)

		snsTopicARN, err := prepareSNSTopic(config.awsKey, config.awsSecret, config.awsRegion, snsTopicName)
		if err != nil {
			return fatalGrimErrorf("error creating SNS topic: %v", err)
		}

		err = prepareSubscription(config.awsKey, config.awsSecret, config.awsRegion, snsTopicARN, i.queue.ARN)
		if err != nil {
			return fatalGrimErrorf("error subscribing Grim queue %q to SNS topic %q: %v", i.queue.ARN, snsTopicARN, err)
		}
		err = prepareAmazonSNSService(localConfig.gitHubToken, repo.owner, repo.name, snsTopicARN, config.awsKey, config.awsSecret, config.awsRegion)
		if err != nil {
			return fatalGrimErrorf("error creating configuring GitHub AmazonSNS service: %v", err)
		}
		topicARNs = append(topicARNs, snsTopicARN)
	}

	err = setPolicy(config.awsKey, config.awsSecret, config.awsRegion, i.queue.ARN, i.queue.URL, topicARNs)
	if err != nil {
		return fatalGrimErrorf("error setting policy for Grim queue %q with topics %v: %v", i.queue.ARN, topicARNs, err)
	}

	return nil
}

// BuildNextInGrimQueue creates or reuses an SQS queue as a source of work.
func (i *Instance) BuildNextInGrimQueue() error {
	if err := i.checkGrimQueue(); err != nil {
		return err
	}

	configRoot := getEffectiveConfigRoot(i.configRoot)

	globalConfig, err := getEffectiveGlobalConfig(configRoot)
	if err != nil {
		return grimErrorf("error while reading config: %v", err)
	}

	message, err := getNextMessage(globalConfig.awsKey, globalConfig.awsSecret, globalConfig.awsRegion, i.queue.URL)
	if err != nil {
		return grimErrorf("error retrieving message from Grim queue %q: %v", i.queue.URL, err)
	}

	if message != "" {
		hook, err := extractHookEvent(message)
		if err != nil {
			return grimErrorf("error extracting hook from message: %v", err)
		}

		if !(hook.eventName == "push" || hook.eventName == "pull_request" && (hook.action == "opened" || hook.action == "reopened" || hook.action == "synchronize")) {
			return nil
		}

		if hook.eventName == "pull_request" {
			sha, err := pollForMergeCommitSha(globalConfig.gitHubToken, hook.owner, hook.repo, hook.prNumber)
			if err != nil {
				return grimErrorf("error getting merge commit sha: %v", err)
			} else if sha == "" {
				return grimErrorf("error getting merge commit sha: field empty")
			}
			hook.ref = sha
		}

		localConfig, err := getEffectiveConfig(configRoot, hook.owner, hook.repo)
		if err != nil {
			return grimErrorf("error while reading config: %v", err)
		}

		return buildForHook(configRoot, localConfig, *hook)
	}

	return nil
}

// BuildRef builds a git ref immediately.
func (i *Instance) BuildRef(owner, repo, ref string) error {
	configRoot := getEffectiveConfigRoot(i.configRoot)

	config, err := getEffectiveConfig(configRoot, owner, repo)
	if err != nil {
		return fatalGrimErrorf("error while reading config: %v", err)
	}

	return buildForHook(configRoot, config, hookEvent{
		owner: owner,
		repo:  repo,
		ref:   ref,
	})
}

func buildForHook(configRoot string, config *effectiveConfig, hook hookEvent) error {
	extraEnv := hook.env()

	// TODO: do something with the err
	notifyPending(config, hook)

	result, err := build(configRoot, config.workspaceRoot, config.pathToCloneIn, hook.owner, hook.repo, extraEnv)
	if err != nil {
		notifyError(config, hook)
		return fatalGrimErrorf("error during %v: %v", describeHook(hook), err)
	}

	var notifyError error
	if result.ExitCode == 0 {
		notifyError = notifySuccess(config, hook)
	} else {
		notifyError = notifyFailure(config, hook)
	}

	err = appendResult(config.resultRoot, hook.owner, hook.repo, *result)
	if err != nil {
		return fatalGrimErrorf("error while storing result: %v", err)
	}

	return notifyError
}

func describeHook(hook hookEvent) string {
	return fmt.Sprintf("build of %v/%v initiated by a %q to %q by %q", hook.owner, hook.repo, hook.eventName, hook.target, hook.userName)
}

func notifyPending(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSPending, fmt.Sprintf("Starting %v", describeHook(hook)), ColorYellow)
}

func notifyError(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSError, fmt.Sprintf("Error during %v", describeHook(hook)), ColorGray)
}

func notifyFailure(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSFailure, fmt.Sprintf("Failure during %v", describeHook(hook)), ColorRed)
}

func notifySuccess(config *effectiveConfig, hook hookEvent) error {
	return notify(config, hook, RSSuccess, fmt.Sprintf("Success after %v", describeHook(hook)), ColorGreen)
}

func notify(config *effectiveConfig, hook hookEvent, state refStatus, message string, color messageColor) error {
	if hook.eventName != "push" && hook.eventName != "pull_request" {
		return nil
	}

	//add grimServerID/grimQueueName to hipchat message for test
	message += "ServerID:" + config.grimServerID
	fmt.Printf("The message is:", message)

	ghErr := setRefStatus(config.gitHubToken, hook.owner, hook.repo, hook.statusRef, state, "", message)

	if config.hipChatToken != "" && config.hipChatRoom != "" {
		err := sendMessageToRoom(config.hipChatToken, config.hipChatRoom, "Grim", message, color)
		if err != nil {
			return err
		}
	}

	return ghErr
}

func (i *Instance) checkGrimQueue() error {
	if i.queue == nil {
		return fatalGrimErrorf("the Grim queue must be prepared first")
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"text/template"

	"github.com/flynn/flynn-controller/client"
)

var cmdHelp = &Command{
	NoClient: true,
	Usage:    "help [topic]",
	Long:     `Help shows usage for a command or other topic.`,
}

func init() {
	cmdHelp.Run = runHelp // break init loop
}

func runHelp(cmd *Command, args []string, client *controller.Client) error {
	if len(args) == 0 {
		printUsage()
		return nil // not os.Exit(2); success
	}
	if len(args) != 1 {
		return errors.New("too many arguments")
	}

	for _, cmd := range commands {
		if cmd.Name() == args[0] {
			cmd.printUsage(false)
			return nil
		}
	}

	fmt.Fprintf(os.Stderr, "Unknown help topic: %q. Run 'flynn help'.\n", args[0])
	os.Exit(2)
	return nil
}

var cmdVersion = &Command{
	Run:      runVersion,
	NoClient: true,
	Usage:    "version",
	Short:    "show flynn version",
	Long:     `Version shows the flynn client version string.`,
}

func runVersion(cmd *Command, args []string, client *controller.Client) error {
	fmt.Println(Version)
	return nil
}

var usageTemplate = template.Must(template.New("usage").Parse(`
Usage: flynn [-a app] [command] [options] [arguments]

Commands:
{{range .Commands}}{{if .Runnable}}
    {{.Name | printf "%-8s"}}  {{.Short}}{{end}}{{end}}
{{range .Plugins}}
    {{.Name | printf "%-8s"}}  {{.Short}} (plugin){{end}}

Run 'flynn help [command]' for details.`[1:]))

func printUsage() {
	usageTemplate.Execute(os.Stdout, struct {
		Commands []*Command
	}{
		commands,
	})
}

func usage() {
	printUsage()
	os.Exit(2)
}
<commit_msg>cli: Remove plugins from usage template<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"text/template"

	"github.com/flynn/flynn-controller/client"
)

var cmdHelp = &Command{
	NoClient: true,
	Usage:    "help [topic]",
	Long:     `Help shows usage for a command or other topic.`,
}

func init() {
	cmdHelp.Run = runHelp // break init loop
}

func runHelp(cmd *Command, args []string, client *controller.Client) error {
	if len(args) == 0 {
		printUsage()
		return nil // not os.Exit(2); success
	}
	if len(args) != 1 {
		return errors.New("too many arguments")
	}

	for _, cmd := range commands {
		if cmd.Name() == args[0] {
			cmd.printUsage(false)
			return nil
		}
	}

	fmt.Fprintf(os.Stderr, "Unknown help topic: %q. Run 'flynn help'.\n", args[0])
	os.Exit(2)
	return nil
}

var cmdVersion = &Command{
	Run:      runVersion,
	NoClient: true,
	Usage:    "version",
	Short:    "show flynn version",
	Long:     `Version shows the flynn client version string.`,
}

func runVersion(cmd *Command, args []string, client *controller.Client) error {
	fmt.Println(Version)
	return nil
}

var usageTemplate = template.Must(template.New("usage").Parse(`
Usage: flynn [-a app] [command] [options] [arguments]

Commands:
{{range .Commands}}{{if .Runnable}}
    {{.Name | printf "%-8s"}}  {{.Short}}{{end}}{{end}}

Run 'flynn help [command]' for details.`[1:]))

func printUsage() {
	usageTemplate.Execute(os.Stdout, struct {
		Commands []*Command
	}{
		commands,
	})
}

func usage() {
	printUsage()
	os.Exit(2)
}
<|endoftext|>


<commit_before>// Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use
// of this source code is governed by the MIT license that can be found in
// the LICENSE file.

package main

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"strings"
	"time"

	rice "github.com/GeertJohan/go.rice"
	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"github.com/go-chi/cors"
	"github.com/go-web/httprl"
)

func initHTTP(closer chan struct{}) {
	r := chi.NewRouter()
	r.Use(middleware.Recoverer)
	r.Use(middleware.Logger)
	r.Use(middleware.StripSlashes)
	r.Use(dbDetailsMiddleware)
	r.Use(middleware.GetHead)

	r.Mount("/dist", http.StripPrefix("/dist", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.FileServer(rice.MustFindBox("public/dist").HTTPBox()).ServeHTTP(w, r)
	})))

	r.Get("/*", func(w http.ResponseWriter, r *http.Request) {
		if strings.HasPrefix(r.URL.Path, "/api") {
			http.NotFound(w, r)
			return
		}

		w.Write(rice.MustFindBox("public/html").MustBytes("index.html"))
	})

	if flags.HTTP.Proxy {
		r.Use(middleware.RealIP)
	}

	if flags.HTTP.CORS == nil || len(flags.HTTP.CORS) == 0 {
		flags.HTTP.CORS = []string{"*"}
	}
	cors := cors.New(cors.Options{
		AllowedOrigins: flags.HTTP.CORS,
		AllowedMethods: []string{"GET"},
		AllowedHeaders: []string{"Accept", "Content-Type"},
		ExposedHeaders: []string{"X-Ratelimit-Limit", "X-Ratelimit-Remaining", "X-Ratelimit-Reset"},
		MaxAge:         3600,
	})

	limiterBackend := httprl.NewMap(10)
	limiter := &httprl.RateLimiter{
		Backend:  limiterBackend,
		Limit:    uint64(flags.HTTP.Limit),
		Interval: 60 * 60, // 1h.
		LimitExceededFunc: func(w http.ResponseWriter, r *http.Request) {
			debug.Printf(
				"connection %s has hit rate limit (limit: %s, reset: %s)",
				r.RemoteAddr,
				w.Header().Get("X-Ratelimit-Limit"),
				w.Header().Get("X-Ratelimit-Reset"),
			)
		},
		KeyMaker: httprl.DefaultKeyMaker, // This uses IP address by default.
	}
	limiterBackend.Start()
	defer limiterBackend.Stop()

	if flags.HTTP.Limit > 0 {
		r.With(cors.Handler, limiter.Handle).Group(registerAPI)
	} else {
		r.With(cors.Handler).Group(registerAPI)
	}

	srv := http.Server{
		Addr:         flags.HTTP.Bind,
		Handler:      r,
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	if flags.HTTP.TLS.Use {
		srv.TLSConfig = &tls.Config{PreferServerCipherSuites: true}

		go srv.ListenAndServeTLS(flags.HTTP.TLS.Cert, flags.HTTP.TLS.Key)
	} else {
		go srv.ListenAndServe()
	}

	<-closer
	fmt.Println("gracefully closing http connections")

	if err := srv.Close(); err != nil {
		debug.Printf("error while stopping http server: %s", err)
	}
}
<commit_msg>print extra debugging info for the http server<commit_after>// Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use
// of this source code is governed by the MIT license that can be found in
// the LICENSE file.

package main

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"strings"
	"time"

	rice "github.com/GeertJohan/go.rice"
	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"github.com/go-chi/cors"
	"github.com/go-web/httprl"
)

func initHTTP(closer chan struct{}) {
	r := chi.NewRouter()
	r.Use(middleware.Recoverer)
	r.Use(middleware.Logger)
	r.Use(middleware.StripSlashes)
	r.Use(dbDetailsMiddleware)
	r.Use(middleware.GetHead)

	r.Mount("/dist", http.StripPrefix("/dist", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.FileServer(rice.MustFindBox("public/dist").HTTPBox()).ServeHTTP(w, r)
	})))

	r.Get("/*", func(w http.ResponseWriter, r *http.Request) {
		if strings.HasPrefix(r.URL.Path, "/api") {
			http.NotFound(w, r)
			return
		}

		w.Write(rice.MustFindBox("public/html").MustBytes("index.html"))
	})

	if flags.HTTP.Proxy {
		r.Use(middleware.RealIP)
	}

	if flags.HTTP.CORS == nil || len(flags.HTTP.CORS) == 0 {
		flags.HTTP.CORS = []string{"*"}
	}
	cors := cors.New(cors.Options{
		AllowedOrigins: flags.HTTP.CORS,
		AllowedMethods: []string{"GET"},
		AllowedHeaders: []string{"Accept", "Content-Type"},
		ExposedHeaders: []string{"X-Ratelimit-Limit", "X-Ratelimit-Remaining", "X-Ratelimit-Reset"},
		MaxAge:         3600,
	})

	limiterBackend := httprl.NewMap(10)
	limiter := &httprl.RateLimiter{
		Backend:  limiterBackend,
		Limit:    uint64(flags.HTTP.Limit),
		Interval: 60 * 60, // 1h.
		LimitExceededFunc: func(w http.ResponseWriter, r *http.Request) {
			debug.Printf(
				"connection %s has hit rate limit (limit: %s, reset: %s)",
				r.RemoteAddr,
				w.Header().Get("X-Ratelimit-Limit"),
				w.Header().Get("X-Ratelimit-Reset"),
			)
		},
		KeyMaker: httprl.DefaultKeyMaker, // This uses IP address by default.
	}
	limiterBackend.Start()
	defer limiterBackend.Stop()

	if flags.HTTP.Limit > 0 {
		r.With(cors.Handler, limiter.Handle).Group(registerAPI)
	} else {
		r.With(cors.Handler).Group(registerAPI)
	}

	srv := http.Server{
		Addr:         flags.HTTP.Bind,
		Handler:      r,
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	if flags.HTTP.TLS.Use {
		srv.TLSConfig = &tls.Config{PreferServerCipherSuites: true}

		go func() {
			debug.Println("starting https server")
			err := srv.ListenAndServeTLS(flags.HTTP.TLS.Cert, flags.HTTP.TLS.Key)
			if err != nil {
				fmt.Printf("error in https server: %s", err)
			}
		}()
	} else {
		go func() {
			debug.Println("starting http server")
			err := srv.ListenAndServe()
			if err != nil {
				fmt.Printf("error in http server: %s", err)
			}
		}()
	}

	<-closer
	fmt.Println("gracefully closing http connections")

	if err := srv.Close(); err != nil {
		debug.Printf("error while stopping http server: %s", err)
	}
}
<|endoftext|>


<commit_before>package chef

import (
	"bytes"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"
)

// ChefVersion that we pretend to emulate
const ChefVersion = "11.12.0"

// Body wraps io.Reader and adds methods for calculating hashes and detecting content
type Body struct {
	io.Reader
}

// AuthConfig representing a client and a private key used for encryption
//  This is embedded in the Client type
type AuthConfig struct {
	PrivateKey *rsa.PrivateKey
	ClientName string
}

// Client is vessel for public methods used against the chef-server
type Client struct {
	Auth    *AuthConfig
	BaseURL *url.URL
	client  *http.Client

	ACLs         *ACLService
	Clients      *ApiClientService
	Cookbooks    *CookbookService
	DataBags     *DataBagService
	Environments *EnvironmentService
	Nodes        *NodeService
	Roles        *RoleService
	Sandboxes    *SandboxService
	Search       *SearchService
}

// Config contains the configuration options for a chef client. This is Used primarily in the NewClient() constructor in order to setup a proper client object
type Config struct {
	// This should be the user ID on the chef server
	Name string

	// This is the plain text private Key for the user
	Key string

	// BaseURL is the chef server URL used to connect too. Is using orgs you should include your org in the url
	BaseURL string

	// When set to false (default) this will enable SSL Cert Verification. If you need to disable Cert Verification set to true
	SkipSSL bool

	// Time to wait in seconds before giving up on a request to the server
	Timeout time.Duration
}

/*
An ErrorResponse reports one or more errors caused by an API request.
Thanks to https://github.com/google/go-github
*/
type ErrorResponse struct {
	Response *http.Response // HTTP response that caused this error
}

// Buffer creates a  byte.Buffer copy from a io.Reader resets read on reader to 0,0
func (body *Body) Buffer() *bytes.Buffer {
	var b bytes.Buffer
	if body.Reader == nil {
		return &b
	}

	b.ReadFrom(body.Reader)
	_, err := body.Reader.(io.Seeker).Seek(0, 0)
	if err != nil {
		log.Fatal(err)
	}
	return &b
}

// Hash calculates the body content hash
func (body *Body) Hash() (h string) {
	b := body.Buffer()
	// empty buffs should return a empty string
	if b.Len() == 0 {
		h = HashStr("")
	}
	h = HashStr(b.String())
	return
}

// ContentType returns the content-type string of Body as detected by http.DetectContentType()
func (body *Body) ContentType() string {
	if json.Unmarshal(body.Buffer().Bytes(), &struct{}{}) == nil {
		return "application/json"
	}
	return http.DetectContentType(body.Buffer().Bytes())
}

func (r *ErrorResponse) Error() string {
	return fmt.Sprintf("%v %v: %d",
		r.Response.Request.Method, r.Response.Request.URL,
		r.Response.StatusCode)
}

// NewClient is the client generator used to instantiate a client for talking to a chef-server
// It is a simple constructor for the Client struct intended as a easy interface for issuing
// signed requests
func NewClient(cfg *Config) (*Client, error) {
	pk, err := PrivateKeyFromString([]byte(cfg.Key))
	if err != nil {
		return nil, err
	}

	baseUrl, _ := url.Parse(cfg.BaseURL)

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: cfg.SkipSSL},
	}

	c := &Client{
		Auth: &AuthConfig{
			PrivateKey: pk,
			ClientName: cfg.Name,
		},
		client: &http.Client{
			Transport: tr,
			Timeout:   cfg.Timeout * time.Second,
		},
		BaseURL: baseUrl,
	}
	c.ACLs = &ACLService{client: c}
	c.Clients = &ApiClientService{client: c}
	c.Cookbooks = &CookbookService{client: c}
	c.DataBags = &DataBagService{client: c}
	c.Environments = &EnvironmentService{client: c}
	c.Nodes = &NodeService{client: c}
	c.Roles = &RoleService{client: c}
	c.Sandboxes = &SandboxService{client: c}
	c.Search = &SearchService{client: c}
	return c, nil
}

// magicRequestDecoder performs a request on an endpoint, and decodes the response into the passed in Type
func (c *Client) magicRequestDecoder(method, path string, body io.Reader, v interface{}) error {
	req, err := c.NewRequest(method, path, body)
	if err != nil {
		return err
	}

	debug("Request: %+v \n", req)
	res, err := c.Do(req, v)
	if res != nil {
		defer res.Body.Close()
	}
	debug("Response: %+v \n", res)
	if err != nil {
		return err
	}
	return err
}

// NewRequest returns a signed request  suitable for the chef server
func (c *Client) NewRequest(method string, requestUrl string, body io.Reader) (*http.Request, error) {
	relativeUrl, err := url.Parse(requestUrl)
	if err != nil {
		return nil, err
	}
	u := c.BaseURL.ResolveReference(relativeUrl)

	// NewRequest uses a new value object of body
	req, err := http.NewRequest(method, u.String(), body)
	if err != nil {
		return nil, err
	}

	// parse and encode Querystring Values
	values := req.URL.Query()
	req.URL.RawQuery = values.Encode()
	debug("Encoded url %+v", u)

	myBody := &Body{body}

	if body != nil {
		// Detect Content-type
		req.Header.Set("Content-Type", myBody.ContentType())
	}

	// Calculate the body hash
	req.Header.Set("X-Ops-Content-Hash", myBody.Hash())

	// don't have to check this works, signRequest only emits error when signing hash is not valid, and we baked that in
	c.Auth.SignRequest(req)
	return req, nil
}

// CheckResponse receives a pointer to a http.Response and generates an Error via unmarshalling
func CheckResponse(r *http.Response) error {
	if c := r.StatusCode; 200 <= c && c <= 299 {
		return nil
	}
	errorResponse := &ErrorResponse{Response: r}
	data, err := ioutil.ReadAll(r.Body)
	if err == nil && data != nil {
		json.Unmarshal(data, errorResponse)
	}
	return errorResponse
}

// Do is used either internally via our magic request shite or a user may use it
func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) {
	res, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	// BUG(fujin) tightly coupled
	err = CheckResponse(res) // <--
	if err != nil {
		return res, err
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			io.Copy(w, res.Body)
		} else {
			err = json.NewDecoder(res.Body).Decode(v)
			if err != nil {
				return res, err
			}
		}
	}
	return res, nil
}

// SignRequest modifies headers of an http.Request
func (ac AuthConfig) SignRequest(request *http.Request) error {
	// sanitize the path for the chef-server
	// chef-server doesn't support '//' in the Hash Path.
	var endpoint string
	if request.URL.Path != "" {
		endpoint = path.Clean(request.URL.Path)
		request.URL.Path = endpoint
	} else {
		endpoint = request.URL.Path
	}

	request.Header.Set("Method", request.Method)
	request.Header.Set("Hashed Path", HashStr(endpoint))
	request.Header.Set("Accept", "application/json")
	request.Header.Set("X-Chef-Version", ChefVersion)
	request.Header.Set("X-Ops-Timestamp", time.Now().UTC().Format(time.RFC3339))
	request.Header.Set("X-Ops-UserId", ac.ClientName)
	request.Header.Set("X-Ops-Sign", "algorithm=sha1;version=1.0")

	// To validate the signature it seems to be very particular
	var content string
	for _, key := range []string{"Method", "Hashed Path", "X-Ops-Content-Hash", "X-Ops-Timestamp", "X-Ops-UserId"} {
		content += fmt.Sprintf("%s:%s\n", key, request.Header.Get(key))
	}
	content = strings.TrimSuffix(content, "\n")
	// generate signed string of headers
	// Since we've gone through additional validation steps above,
	// we shouldn't get an error at this point
	signature, err := GenerateSignature(ac.PrivateKey, content)
	if err != nil {
		return err
	}

	// TODO: THIS IS CHEF PROTOCOL SPECIFIC
	// Signature is made up of n 60 length chunks
	base64sig := Base64BlockEncode(signature, 60)

	// roll over the auth slice and add the apropriate header
	for index, value := range base64sig {
		request.Header.Set(fmt.Sprintf("X-Ops-Authorization-%d", index+1), string(value))
	}

	return nil
}

// PrivateKeyFromString parses an RSA private key from a string
func PrivateKeyFromString(key []byte) (*rsa.PrivateKey, error) {
	block, _ := pem.Decode(key)
	if block == nil {
		return nil, fmt.Errorf("block size invalid for '%s'", string(key))
	}
	rsaKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	return rsaKey, nil
}
<commit_msg>make go-chef HTTP headers compliant with RFC 2616 (HTTP/1.1) and go1.6<commit_after>package chef

import (
	"bytes"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"
)

// ChefVersion that we pretend to emulate
const ChefVersion = "11.12.0"

// Body wraps io.Reader and adds methods for calculating hashes and detecting content
type Body struct {
	io.Reader
}

// AuthConfig representing a client and a private key used for encryption
//  This is embedded in the Client type
type AuthConfig struct {
	PrivateKey *rsa.PrivateKey
	ClientName string
}

// Client is vessel for public methods used against the chef-server
type Client struct {
	Auth    *AuthConfig
	BaseURL *url.URL
	client  *http.Client

	ACLs         *ACLService
	Clients      *ApiClientService
	Cookbooks    *CookbookService
	DataBags     *DataBagService
	Environments *EnvironmentService
	Nodes        *NodeService
	Roles        *RoleService
	Sandboxes    *SandboxService
	Search       *SearchService
}

// Config contains the configuration options for a chef client. This is Used primarily in the NewClient() constructor in order to setup a proper client object
type Config struct {
	// This should be the user ID on the chef server
	Name string

	// This is the plain text private Key for the user
	Key string

	// BaseURL is the chef server URL used to connect too. Is using orgs you should include your org in the url
	BaseURL string

	// When set to false (default) this will enable SSL Cert Verification. If you need to disable Cert Verification set to true
	SkipSSL bool

	// Time to wait in seconds before giving up on a request to the server
	Timeout time.Duration
}

/*
An ErrorResponse reports one or more errors caused by an API request.
Thanks to https://github.com/google/go-github
*/
type ErrorResponse struct {
	Response *http.Response // HTTP response that caused this error
}

// Buffer creates a  byte.Buffer copy from a io.Reader resets read on reader to 0,0
func (body *Body) Buffer() *bytes.Buffer {
	var b bytes.Buffer
	if body.Reader == nil {
		return &b
	}

	b.ReadFrom(body.Reader)
	_, err := body.Reader.(io.Seeker).Seek(0, 0)
	if err != nil {
		log.Fatal(err)
	}
	return &b
}

// Hash calculates the body content hash
func (body *Body) Hash() (h string) {
	b := body.Buffer()
	// empty buffs should return a empty string
	if b.Len() == 0 {
		h = HashStr("")
	}
	h = HashStr(b.String())
	return
}

// ContentType returns the content-type string of Body as detected by http.DetectContentType()
func (body *Body) ContentType() string {
	if json.Unmarshal(body.Buffer().Bytes(), &struct{}{}) == nil {
		return "application/json"
	}
	return http.DetectContentType(body.Buffer().Bytes())
}

func (r *ErrorResponse) Error() string {
	return fmt.Sprintf("%v %v: %d",
		r.Response.Request.Method, r.Response.Request.URL,
		r.Response.StatusCode)
}

// NewClient is the client generator used to instantiate a client for talking to a chef-server
// It is a simple constructor for the Client struct intended as a easy interface for issuing
// signed requests
func NewClient(cfg *Config) (*Client, error) {
	pk, err := PrivateKeyFromString([]byte(cfg.Key))
	if err != nil {
		return nil, err
	}

	baseUrl, _ := url.Parse(cfg.BaseURL)

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: cfg.SkipSSL},
	}

	c := &Client{
		Auth: &AuthConfig{
			PrivateKey: pk,
			ClientName: cfg.Name,
		},
		client: &http.Client{
			Transport: tr,
			Timeout:   cfg.Timeout * time.Second,
		},
		BaseURL: baseUrl,
	}
	c.ACLs = &ACLService{client: c}
	c.Clients = &ApiClientService{client: c}
	c.Cookbooks = &CookbookService{client: c}
	c.DataBags = &DataBagService{client: c}
	c.Environments = &EnvironmentService{client: c}
	c.Nodes = &NodeService{client: c}
	c.Roles = &RoleService{client: c}
	c.Sandboxes = &SandboxService{client: c}
	c.Search = &SearchService{client: c}
	return c, nil
}

// magicRequestDecoder performs a request on an endpoint, and decodes the response into the passed in Type
func (c *Client) magicRequestDecoder(method, path string, body io.Reader, v interface{}) error {
	req, err := c.NewRequest(method, path, body)
	if err != nil {
		return err
	}

	debug("Request: %+v \n", req)
	res, err := c.Do(req, v)
	if res != nil {
		defer res.Body.Close()
	}
	debug("Response: %+v \n", res)
	if err != nil {
		return err
	}
	return err
}

// NewRequest returns a signed request  suitable for the chef server
func (c *Client) NewRequest(method string, requestUrl string, body io.Reader) (*http.Request, error) {
	relativeUrl, err := url.Parse(requestUrl)
	if err != nil {
		return nil, err
	}
	u := c.BaseURL.ResolveReference(relativeUrl)

	// NewRequest uses a new value object of body
	req, err := http.NewRequest(method, u.String(), body)
	if err != nil {
		return nil, err
	}

	// parse and encode Querystring Values
	values := req.URL.Query()
	req.URL.RawQuery = values.Encode()
	debug("Encoded url %+v", u)

	myBody := &Body{body}

	if body != nil {
		// Detect Content-type
		req.Header.Set("Content-Type", myBody.ContentType())
	}

	// Calculate the body hash
	req.Header.Set("X-Ops-Content-Hash", myBody.Hash())

	// don't have to check this works, signRequest only emits error when signing hash is not valid, and we baked that in
	c.Auth.SignRequest(req)
	return req, nil
}

// CheckResponse receives a pointer to a http.Response and generates an Error via unmarshalling
func CheckResponse(r *http.Response) error {
	if c := r.StatusCode; 200 <= c && c <= 299 {
		return nil
	}
	errorResponse := &ErrorResponse{Response: r}
	data, err := ioutil.ReadAll(r.Body)
	if err == nil && data != nil {
		json.Unmarshal(data, errorResponse)
	}
	return errorResponse
}

// Do is used either internally via our magic request shite or a user may use it
func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) {
	res, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	// BUG(fujin) tightly coupled
	err = CheckResponse(res) // <--
	if err != nil {
		return res, err
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			io.Copy(w, res.Body)
		} else {
			err = json.NewDecoder(res.Body).Decode(v)
			if err != nil {
				return res, err
			}
		}
	}
	return res, nil
}

// SignRequest modifies headers of an http.Request
func (ac AuthConfig) SignRequest(request *http.Request) error {
	// sanitize the path for the chef-server
	// chef-server doesn't support '//' in the Hash Path.
	var endpoint string
	if request.URL.Path != "" {
		endpoint = path.Clean(request.URL.Path)
		request.URL.Path = endpoint
	} else {
		endpoint = request.URL.Path
	}

	vals := map[string]string{
		"Method":             request.Method,
		"Hashed Path":        HashStr(endpoint),
		"Accept":             "application/json",
		"X-Chef-Version":     ChefVersion,
		"X-Ops-Timestamp":    time.Now().UTC().Format(time.RFC3339),
		"X-Ops-UserId":       ac.ClientName,
		"X-Ops-Sign":         "algorithm=sha1;version=1.0",
		"X-Ops-Content-Hash": request.Header.Get("X-Ops-Content-Hash"),
	}

	for _, key := range []string{"Method", "Accept", "X-Chef-Version", "X-Ops-Timestamp", "X-Ops-UserId", "X-Ops-Sign"} {
		request.Header.Set(key, vals[key])
	}

	// To validate the signature it seems to be very particular
	var content string
	for _, key := range []string{"Method", "Hashed Path", "X-Ops-Content-Hash", "X-Ops-Timestamp", "X-Ops-UserId"} {
		content += fmt.Sprintf("%s:%s\n", key, vals[key])
	}
	content = strings.TrimSuffix(content, "\n")
	// generate signed string of headers
	// Since we've gone through additional validation steps above,
	// we shouldn't get an error at this point
	signature, err := GenerateSignature(ac.PrivateKey, content)
	if err != nil {
		return err
	}

	// TODO: THIS IS CHEF PROTOCOL SPECIFIC
	// Signature is made up of n 60 length chunks
	base64sig := Base64BlockEncode(signature, 60)

	// roll over the auth slice and add the apropriate header
	for index, value := range base64sig {
		request.Header.Set(fmt.Sprintf("X-Ops-Authorization-%d", index+1), string(value))
	}

	return nil
}

// PrivateKeyFromString parses an RSA private key from a string
func PrivateKeyFromString(key []byte) (*rsa.PrivateKey, error) {
	block, _ := pem.Decode(key)
	if block == nil {
		return nil, fmt.Errorf("block size invalid for '%s'", string(key))
	}
	rsaKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	return rsaKey, nil
}
<|endoftext|>