<commit_before>package main

import (
	"gopkg.in/jcelliott/turnpike.v2"
	"log"
	"strconv"
)

const (
	POLONIEX_WEBSOCKET_ADDRESS  = "wss://api.poloniex.com"
	POLONIEX_WEBSOCKET_REALM    = "realm1"
	POLONIEX_WEBSOCKET_TICKER   = "ticker"
	POLONIEX_WEBSOCKET_TROLLBOX = "trollbox"
)

type PoloniexWebsocketTicker struct {
	CurrencyPair  string
	Last          float64
	LowestAsk     float64
	HighestBid    float64
	PercentChange float64
	BaseVolume    float64
	QuoteVolume   float64
	IsFrozen      bool
	High          float64
	Low           float64
}

func PoloniexOnTicker(args []interface{}, kwargs map[string]interface{}) {
	ticker := PoloniexWebsocketTicker{}
	ticker.CurrencyPair = args[0].(string)
	ticker.Last, _ = strconv.ParseFloat(args[1].(string), 64)
	ticker.LowestAsk, _ = strconv.ParseFloat(args[2].(string), 64)
	ticker.HighestBid, _ = strconv.ParseFloat(args[3].(string), 64)
	ticker.PercentChange, _ = strconv.ParseFloat(args[4].(string), 64)
	ticker.BaseVolume, _ = strconv.ParseFloat(args[5].(string), 64)
	ticker.QuoteVolume, _ = strconv.ParseFloat(args[6].(string), 64)

	if args[7].(float64) != 0 {
		ticker.IsFrozen = true
	} else {
		ticker.IsFrozen = false
	}

	ticker.High, _ = strconv.ParseFloat(args[8].(string), 64)
	ticker.Low, _ = strconv.ParseFloat(args[9].(string), 64)
}

type PoloniexWebsocketTrollboxMessage struct {
	MessageNumber float64
	Username      string
	Message       string
	Reputation    float64
}

func PoloniexOnTrollbox(args []interface{}, kwargs map[string]interface{}) {
	message := PoloniexWebsocketTrollboxMessage{}
	message.MessageNumber, _ = args[1].(float64)
	message.Username = args[2].(string)
	message.Message = args[3].(string)
	message.Reputation = args[4].(float64)
}

func PoloniexOnDepthOrTrade(args []interface{}, kwargs map[string]interface{}) {
	for x := range args {
		data := args[x].(map[string]interface{})
		msgData := data["data"].(map[string]interface{})
		msgType := data["type"].(string)

		switch msgType {
		case "orderBookModify":
			{
				type PoloniexWebsocketOrderbookModify struct {
					Type   string
					Rate   float64
					Amount float64
				}

				orderModify := PoloniexWebsocketOrderbookModify{}
				orderModify.Type = msgData["type"].(string)

				rateStr := msgData["rate"].(string)
				orderModify.Rate, _ = strconv.ParseFloat(rateStr, 64)

				amountStr := msgData["amount"].(string)
				orderModify.Amount, _ = strconv.ParseFloat(amountStr, 64)
			}
		case "orderBookRemove":
			{
				type PoloniexWebsocketOrderbookRemove struct {
					Type string
					Rate float64
				}

				orderRemoval := PoloniexWebsocketOrderbookRemove{}
				orderRemoval.Type = msgData["type"].(string)

				rateStr := msgData["rate"].(string)
				orderRemoval.Rate, _ = strconv.ParseFloat(rateStr, 64)
			}
		case "newTrade":
			{
				type PoloniexWebsocketNewTrade struct {
					Type    string
					TradeID int64
					Rate    float64
					Amount  float64
					Date    string
					Total   float64
				}

				trade := PoloniexWebsocketNewTrade{}
				trade.Type = msgData["type"].(string)

				tradeIDstr := msgData["tradeID"].(string)
				trade.TradeID, _ = strconv.ParseInt(tradeIDstr, 10, 64)

				rateStr := msgData["rate"].(string)
				trade.Rate, _ = strconv.ParseFloat(rateStr, 64)

				amountStr := msgData["amount"].(string)
				trade.Amount, _ = strconv.ParseFloat(amountStr, 64)

				totalStr := msgData["total"].(string)
				trade.Rate, _ = strconv.ParseFloat(totalStr, 64)

				trade.Date = msgData["date"].(string)
			}
		}
	}
}

func (p *Poloniex) WebsocketClient() {
	for p.Enabled && p.Websocket {
		c, err := turnpike.NewWebsocketClient(turnpike.JSON, POLONIEX_WEBSOCKET_ADDRESS)
		if err != nil {
			log.Printf("%s Unable to connect to Websocket. Error: %s\n", p.GetName(), err)
			continue
		}

		if p.Verbose {
			log.Printf("%s Connected to Websocket.\n", p.GetName())
		}

		_, err = c.JoinRealm(POLONIEX_WEBSOCKET_REALM, nil)
		if err != nil {
			log.Printf("%s Unable to join realm. Error: %s\n", p.GetName(), err)
			continue
		}

		if p.Verbose {
			log.Printf("%s Joined Websocket realm.\n", p.GetName())
		}

		c.ReceiveDone = make(chan bool)

		if err := c.Subscribe(POLONIEX_WEBSOCKET_TICKER, PoloniexOnTicker); err != nil {
			log.Printf("%s Error subscribing to ticker channel: %s\n", p.GetName(), err)
		}

		if err := c.Subscribe(POLONIEX_WEBSOCKET_TROLLBOX, PoloniexOnTrollbox); err != nil {
			log.Printf("%s Error subscribing to trollbox channel: %s\n", p.GetName(), err)
		}

		for x := range p.EnabledPairs {
			currency := p.EnabledPairs[x]
			if err := c.Subscribe(currency, PoloniexOnDepthOrTrade); err != nil {
				log.Printf("%s Error subscribing to %s channel: %s\n", p.GetName(), currency, err)
			}
		}

		if p.Verbose {
			log.Printf("%s Subscribed to websocket channels.\n", p.GetName())
		}

		<-c.ReceiveDone
		log.Printf("%s Websocket client disconnected.\n", p.GetName())
	}
}
<commit_msg>Added support for latest turnpike library.<commit_after>package main

import (
	"gopkg.in/jcelliott/turnpike.v2"
	"log"
	"strconv"
)

const (
	POLONIEX_WEBSOCKET_ADDRESS  = "wss://api.poloniex.com"
	POLONIEX_WEBSOCKET_REALM    = "realm1"
	POLONIEX_WEBSOCKET_TICKER   = "ticker"
	POLONIEX_WEBSOCKET_TROLLBOX = "trollbox"
)

type PoloniexWebsocketTicker struct {
	CurrencyPair  string
	Last          float64
	LowestAsk     float64
	HighestBid    float64
	PercentChange float64
	BaseVolume    float64
	QuoteVolume   float64
	IsFrozen      bool
	High          float64
	Low           float64
}

func PoloniexOnTicker(args []interface{}, kwargs map[string]interface{}) {
	ticker := PoloniexWebsocketTicker{}
	ticker.CurrencyPair = args[0].(string)
	ticker.Last, _ = strconv.ParseFloat(args[1].(string), 64)
	ticker.LowestAsk, _ = strconv.ParseFloat(args[2].(string), 64)
	ticker.HighestBid, _ = strconv.ParseFloat(args[3].(string), 64)
	ticker.PercentChange, _ = strconv.ParseFloat(args[4].(string), 64)
	ticker.BaseVolume, _ = strconv.ParseFloat(args[5].(string), 64)
	ticker.QuoteVolume, _ = strconv.ParseFloat(args[6].(string), 64)

	if args[7].(float64) != 0 {
		ticker.IsFrozen = true
	} else {
		ticker.IsFrozen = false
	}

	ticker.High, _ = strconv.ParseFloat(args[8].(string), 64)
	ticker.Low, _ = strconv.ParseFloat(args[9].(string), 64)
}

type PoloniexWebsocketTrollboxMessage struct {
	MessageNumber float64
	Username      string
	Message       string
	Reputation    float64
}

func PoloniexOnTrollbox(args []interface{}, kwargs map[string]interface{}) {
	message := PoloniexWebsocketTrollboxMessage{}
	message.MessageNumber, _ = args[1].(float64)
	message.Username = args[2].(string)
	message.Message = args[3].(string)
	message.Reputation = args[4].(float64)
}

func PoloniexOnDepthOrTrade(args []interface{}, kwargs map[string]interface{}) {
	for x := range args {
		data := args[x].(map[string]interface{})
		msgData := data["data"].(map[string]interface{})
		msgType := data["type"].(string)

		switch msgType {
		case "orderBookModify":
			{
				type PoloniexWebsocketOrderbookModify struct {
					Type   string
					Rate   float64
					Amount float64
				}

				orderModify := PoloniexWebsocketOrderbookModify{}
				orderModify.Type = msgData["type"].(string)

				rateStr := msgData["rate"].(string)
				orderModify.Rate, _ = strconv.ParseFloat(rateStr, 64)

				amountStr := msgData["amount"].(string)
				orderModify.Amount, _ = strconv.ParseFloat(amountStr, 64)
			}
		case "orderBookRemove":
			{
				type PoloniexWebsocketOrderbookRemove struct {
					Type string
					Rate float64
				}

				orderRemoval := PoloniexWebsocketOrderbookRemove{}
				orderRemoval.Type = msgData["type"].(string)

				rateStr := msgData["rate"].(string)
				orderRemoval.Rate, _ = strconv.ParseFloat(rateStr, 64)
			}
		case "newTrade":
			{
				type PoloniexWebsocketNewTrade struct {
					Type    string
					TradeID int64
					Rate    float64
					Amount  float64
					Date    string
					Total   float64
				}

				trade := PoloniexWebsocketNewTrade{}
				trade.Type = msgData["type"].(string)

				tradeIDstr := msgData["tradeID"].(string)
				trade.TradeID, _ = strconv.ParseInt(tradeIDstr, 10, 64)

				rateStr := msgData["rate"].(string)
				trade.Rate, _ = strconv.ParseFloat(rateStr, 64)

				amountStr := msgData["amount"].(string)
				trade.Amount, _ = strconv.ParseFloat(amountStr, 64)

				totalStr := msgData["total"].(string)
				trade.Rate, _ = strconv.ParseFloat(totalStr, 64)

				trade.Date = msgData["date"].(string)
			}
		}
	}
}

func (p *Poloniex) WebsocketClient() {
	for p.Enabled && p.Websocket {
		c, err := turnpike.NewWebsocketClient(turnpike.JSON, POLONIEX_WEBSOCKET_ADDRESS, nil)
		if err != nil {
			log.Printf("%s Unable to connect to Websocket. Error: %s\n", p.GetName(), err)
			continue
		}

		if p.Verbose {
			log.Printf("%s Connected to Websocket.\n", p.GetName())
		}

		_, err = c.JoinRealm(POLONIEX_WEBSOCKET_REALM, nil)
		if err != nil {
			log.Printf("%s Unable to join realm. Error: %s\n", p.GetName(), err)
			continue
		}

		if p.Verbose {
			log.Printf("%s Joined Websocket realm.\n", p.GetName())
		}

		c.ReceiveDone = make(chan bool)

		if err := c.Subscribe(POLONIEX_WEBSOCKET_TICKER, PoloniexOnTicker); err != nil {
			log.Printf("%s Error subscribing to ticker channel: %s\n", p.GetName(), err)
		}

		if err := c.Subscribe(POLONIEX_WEBSOCKET_TROLLBOX, PoloniexOnTrollbox); err != nil {
			log.Printf("%s Error subscribing to trollbox channel: %s\n", p.GetName(), err)
		}

		for x := range p.EnabledPairs {
			currency := p.EnabledPairs[x]
			if err := c.Subscribe(currency, PoloniexOnDepthOrTrade); err != nil {
				log.Printf("%s Error subscribing to %s channel: %s\n", p.GetName(), currency, err)
			}
		}

		if p.Verbose {
			log.Printf("%s Subscribed to websocket channels.\n", p.GetName())
		}

		<-c.ReceiveDone
		log.Printf("%s Websocket client disconnected.\n", p.GetName())
	}
}
<|endoftext|>


<commit_before>/*--------------------------------------------------------*\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: https://hprose.com                     |
|                                                          |
| encoding/error.go                                        |
|                                                          |
| LastModified: Mar 22, 2020                               |
| Author: Ma Bingyao <andot@hprose.com>                    |
|                                                          |
\*________________________________________________________*/

package encoding

import (
	"reflect"
)

// An UnsupportedTypeError is returned by Encode/Marshal when attempting
// to encode an unsupported value type.
type UnsupportedTypeError struct {
	Type reflect.Type
}

func (e *UnsupportedTypeError) Error() string {
	return "hprose: unsupported type: " + e.Type.String()
}
<commit_msg>Update error.go<commit_after>/*--------------------------------------------------------*\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: https://hprose.com                     |
|                                                          |
| encoding/error.go                                        |
|                                                          |
| LastModified: Mar 22, 2020                               |
| Author: Ma Bingyao <andot@hprose.com>                    |
|                                                          |
\*________________________________________________________*/

package encoding

import (
	"reflect"
)

// An UnsupportedTypeError is returned by Encode/Marshal when attempting
// to encode an unsupported value type.
type UnsupportedTypeError struct {
	Type reflect.Type
}

func (e *UnsupportedTypeError) Error() string {
	return "hprose/encoding: unsupported type: " + e.Type.String()
}
<|endoftext|>


<commit_before>package config

import (
	"testing"

	"github.com/vaughan0/go-ini"
	config2 "go.zhuzi.me/config/config"
)

// Test_ManualParser 测试manual解析
func Test_ManualParser(t *testing.T) {
	p := config2.NewManualParser()
	p.SetConfig("app", "system", "version", "beta0.1")
	if err := Init(false, p); err != nil {
		t.Error(err)
	}
	if v, ok := Data("app").Get("system", "version"); !ok {
		t.Error("没有找到设置的配置")
	} else if v != "beta0.1" {
		t.Error("找到的值与预设值不一致，设置的值：beta0.1,获取到的值:", v)
	}
	t.Log("test config success")

	p = config2.NewManualParser()
	p.SetSection("app", "system", ini.Section{"appName": "config"})
	if err := Init(false, p); err != nil {
		t.Error(err)
	}
	if v, ok := Data("app").Get("system", "appName"); !ok {
		t.Error("没有找到设置的配置")
	} else if v != "config" {
		t.Error("找到的值与预设值不一致，设置的值：config,获取到的值:", v)
	}
	t.Log("test section success")

	p = config2.NewManualParser()
	p.SetFile("app", ini.File{"system": ini.Section{"author": "scofield"}})
	if err := Init(false, p); err != nil {
		t.Error(err)
	}
	if v, ok := Data("app").Get("system", "author"); !ok {
		t.Error("没有找到设置的配置")
	} else if v != "scofield" {
		t.Error("找到的值与预设值不一致，设置的值：scofield,获取到的值:", v)
	}
	t.Log("test app success")

}
<commit_msg>fix bug<commit_after>package config

import (
	"testing"

	"github.com/vaughan0/go-ini"
)

// Test_ManualParser 测试manual解析
func Test_ManualParser(t *testing.T) {
	p := NewManualParser()
	p.SetConfig("app", "system", "version", "beta0.1")
	if err := Init(false, p); err != nil {
		t.Error(err)
	}
	if v, ok := Data("app").Get("system", "version"); !ok {
		t.Error("没有找到设置的配置")
	} else if v != "beta0.1" {
		t.Error("找到的值与预设值不一致，设置的值：beta0.1,获取到的值:", v)
	}
	t.Log("test config success")

	p = NewManualParser()
	p.SetSection("app", "system", ini.Section{"appName": "config"})
	if err := Init(false, p); err != nil {
		t.Error(err)
	}
	if v, ok := Data("app").Get("system", "appName"); !ok {
		t.Error("没有找到设置的配置")
	} else if v != "config" {
		t.Error("找到的值与预设值不一致，设置的值：config,获取到的值:", v)
	}
	t.Log("test section success")

	p = NewManualParser()
	p.SetFile("app", ini.File{"system": ini.Section{"author": "scofield"}})
	if err := Init(false, p); err != nil {
		t.Error(err)
	}
	if v, ok := Data("app").Get("system", "author"); !ok {
		t.Error("没有找到设置的配置")
	} else if v != "scofield" {
		t.Error("找到的值与预设值不一致，设置的值：scofield,获取到的值:", v)
	}
	t.Log("test app success")

}
<|endoftext|>


<commit_before>package envstruct_test

import (
	"envstruct"
	"os"

	"fmt"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type TestStruct struct {
	NonEnvThing   string
	StringThing   string `env:"string_thing"`
	RequiredThing string `env:"required_thing,required"`

	BoolThing bool `env:"bool_thing"`

	IntThing    int    `env:"int_thing"`
	Int8Thing   int8   `env:"int8_thing"`
	Int16Thing  int16  `env:"int16_thing"`
	Int32Thing  int32  `env:"int32_thing"`
	Int64Thing  int64  `env:"int64_thing"`
	UintThing   uint   `env:"uint_thing"`
	Uint8Thing  uint8  `env:"uint8_thing"`
	Uint16Thing uint16 `env:"uint16_thing"`
	Uint32Thing uint32 `env:"uint32_thing"`
	Uint64Thing uint64 `env:"uint64_thing"`

	StringSliceThing []string `env:"string_slice_thing"`
	IntSliceThing    []int    `env:"int_slice_thing"`
}

var _ = Describe("envstruct", func() {
	Describe("Load()", func() {
		var (
			ts        TestStruct
			loadError error
			envVars   map[string]string
		)

		BeforeEach(func() {
			envVars = map[string]string{
				"STRING_THING":       "stringy thingy",
				"REQUIRED_THING":     "im so required",
				"BOOL_THING":         "true",
				"INT_THING":          "100",
				"INT8_THING":         "20",
				"INT16_THING":        "2000",
				"INT32_THING":        "200000",
				"INT64_THING":        "200000000",
				"UINT_THING":         "100",
				"UINT8_THING":        "20",
				"UINT16_THING":       "2000",
				"UINT32_THING":       "200000",
				"UINT64_THING":       "200000000",
				"STRING_SLICE_THING": "one,two,three",
				"INT_SLICE_THING":    "1,2,3",
			}
		})

		JustBeforeEach(func() {
			for k, v := range envVars {
				os.Setenv(k, v)
			}
		})

		Context("when load is successfull", func() {
			JustBeforeEach(func() {
				loadError = envstruct.Load(&ts)
			})

			AfterEach(func() {
				for k := range envVars {
					os.Setenv(k, "")
				}
			})

			It("does not return an error", func() {
				Expect(loadError).ToNot(HaveOccurred())
			})

			Context("with strings", func() {
				It("populates the string thing", func() {
					Expect(ts.StringThing).To(Equal("stringy thingy"))
				})
			})

			Context("with bools", func() {
				Context("with 'true'", func() {
					It("is true", func() {
						Expect(ts.BoolThing).To(BeTrue())
					})
				})

				Context("with 'false'", func() {
					BeforeEach(func() {
						envVars["BOOL_THING"] = "false"
					})

					It("is true", func() {
						Expect(ts.BoolThing).To(BeFalse())
					})
				})

				Context("with '1'", func() {
					BeforeEach(func() {
						envVars["BOOL_THING"] = "1"
					})

					It("is true", func() {
						Expect(ts.BoolThing).To(BeTrue())
					})
				})

				Context("with '0'", func() {
					BeforeEach(func() {
						envVars["BOOL_THING"] = "0"
					})

					It("is false", func() {
						Expect(ts.BoolThing).To(BeFalse())
					})
				})
			})

			Context("with ints", func() {
				It("populates the int thing", func() {
					Expect(ts.IntThing).To(Equal(100))
				})

				It("populates the int 8 thing", func() {
					Expect(ts.Int8Thing).To(Equal(int8(20)))
				})

				It("populates the int 16 thing", func() {
					Expect(ts.Int16Thing).To(Equal(int16(2000)))
				})

				It("populates the int 32 thing", func() {
					Expect(ts.Int32Thing).To(Equal(int32(200000)))
				})

				It("populates the int 64 thing", func() {
					Expect(ts.Int64Thing).To(Equal(int64(200000000)))
				})
			})

			Context("with uints", func() {
				It("populates the uint thing", func() {
					Expect(ts.UintThing).To(Equal(uint(100)))
				})

				It("populates the uint 8 thing", func() {
					Expect(ts.Uint8Thing).To(Equal(uint8(20)))
				})

				It("populates the uint 16 thing", func() {
					Expect(ts.Uint16Thing).To(Equal(uint16(2000)))
				})

				It("populates the uint 32 thing", func() {
					Expect(ts.Uint32Thing).To(Equal(uint32(200000)))
				})

				It("populates the uint 64 thing", func() {
					Expect(ts.Uint64Thing).To(Equal(uint64(200000000)))
				})
			})

			Context("with comma separated strings", func() {
				Context("slice of strings", func() {
					It("populates a slice of strings", func() {
						Expect(ts.StringSliceThing).To(Equal([]string{"one", "two", "three"}))
					})

					Context("with leading and trailing spaces", func() {
						BeforeEach(func() {
							envVars["STRING_SLICE_THING"] = "one , two , three"
						})

						It("populates a slice of strings", func() {
							Expect(ts.StringSliceThing).To(Equal([]string{"one", "two", "three"}))
						})
					})
				})

				Context("slice of ints", func() {
					It("populates a slice of ints", func() {
						Expect(ts.IntSliceThing).To(Equal([]int{1, 2, 3}))
					})
				})
			})
		})

		Context("when load is unsuccessfull", func() {
			Context("when a required environment variable is not given", func() {
				BeforeEach(func() {
					envVars["REQUIRED_THING"] = ""
				})

				It("returns a validation error", func() {
					loadError = envstruct.Load(&ts)

					Expect(loadError).To(MatchError(fmt.Errorf("REQUIRED_THING is required but was empty")))
				})
			})

			Context("with an invalid int", func() {
				BeforeEach(func() {
					envVars["INT_THING"] = "Hello!"
				})

				It("returns an error", func() {
					Expect(envstruct.Load(&ts)).ToNot(Succeed())
				})
			})

			Context("with an invalid uint", func() {
				BeforeEach(func() {
					envVars["UINT_THING"] = "Hello!"
				})

				It("returns an error", func() {
					Expect(envstruct.Load(&ts)).ToNot(Succeed())
				})
			})
		})
	})
})
<commit_msg>Fix typo<commit_after>package envstruct_test

import (
	"envstruct"
	"os"

	"fmt"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type TestStruct struct {
	NonEnvThing   string
	StringThing   string `env:"string_thing"`
	RequiredThing string `env:"required_thing,required"`

	BoolThing bool `env:"bool_thing"`

	IntThing    int    `env:"int_thing"`
	Int8Thing   int8   `env:"int8_thing"`
	Int16Thing  int16  `env:"int16_thing"`
	Int32Thing  int32  `env:"int32_thing"`
	Int64Thing  int64  `env:"int64_thing"`
	UintThing   uint   `env:"uint_thing"`
	Uint8Thing  uint8  `env:"uint8_thing"`
	Uint16Thing uint16 `env:"uint16_thing"`
	Uint32Thing uint32 `env:"uint32_thing"`
	Uint64Thing uint64 `env:"uint64_thing"`

	StringSliceThing []string `env:"string_slice_thing"`
	IntSliceThing    []int    `env:"int_slice_thing"`
}

var _ = Describe("envstruct", func() {
	Describe("Load()", func() {
		var (
			ts        TestStruct
			loadError error
			envVars   map[string]string
		)

		BeforeEach(func() {
			envVars = map[string]string{
				"STRING_THING":       "stringy thingy",
				"REQUIRED_THING":     "im so required",
				"BOOL_THING":         "true",
				"INT_THING":          "100",
				"INT8_THING":         "20",
				"INT16_THING":        "2000",
				"INT32_THING":        "200000",
				"INT64_THING":        "200000000",
				"UINT_THING":         "100",
				"UINT8_THING":        "20",
				"UINT16_THING":       "2000",
				"UINT32_THING":       "200000",
				"UINT64_THING":       "200000000",
				"STRING_SLICE_THING": "one,two,three",
				"INT_SLICE_THING":    "1,2,3",
			}
		})

		JustBeforeEach(func() {
			for k, v := range envVars {
				os.Setenv(k, v)
			}
		})

		Context("when load is successful", func() {
			JustBeforeEach(func() {
				loadError = envstruct.Load(&ts)
			})

			AfterEach(func() {
				for k := range envVars {
					os.Setenv(k, "")
				}
			})

			It("does not return an error", func() {
				Expect(loadError).ToNot(HaveOccurred())
			})

			Context("with strings", func() {
				It("populates the string thing", func() {
					Expect(ts.StringThing).To(Equal("stringy thingy"))
				})
			})

			Context("with bools", func() {
				Context("with 'true'", func() {
					It("is true", func() {
						Expect(ts.BoolThing).To(BeTrue())
					})
				})

				Context("with 'false'", func() {
					BeforeEach(func() {
						envVars["BOOL_THING"] = "false"
					})

					It("is true", func() {
						Expect(ts.BoolThing).To(BeFalse())
					})
				})

				Context("with '1'", func() {
					BeforeEach(func() {
						envVars["BOOL_THING"] = "1"
					})

					It("is true", func() {
						Expect(ts.BoolThing).To(BeTrue())
					})
				})

				Context("with '0'", func() {
					BeforeEach(func() {
						envVars["BOOL_THING"] = "0"
					})

					It("is false", func() {
						Expect(ts.BoolThing).To(BeFalse())
					})
				})
			})

			Context("with ints", func() {
				It("populates the int thing", func() {
					Expect(ts.IntThing).To(Equal(100))
				})

				It("populates the int 8 thing", func() {
					Expect(ts.Int8Thing).To(Equal(int8(20)))
				})

				It("populates the int 16 thing", func() {
					Expect(ts.Int16Thing).To(Equal(int16(2000)))
				})

				It("populates the int 32 thing", func() {
					Expect(ts.Int32Thing).To(Equal(int32(200000)))
				})

				It("populates the int 64 thing", func() {
					Expect(ts.Int64Thing).To(Equal(int64(200000000)))
				})
			})

			Context("with uints", func() {
				It("populates the uint thing", func() {
					Expect(ts.UintThing).To(Equal(uint(100)))
				})

				It("populates the uint 8 thing", func() {
					Expect(ts.Uint8Thing).To(Equal(uint8(20)))
				})

				It("populates the uint 16 thing", func() {
					Expect(ts.Uint16Thing).To(Equal(uint16(2000)))
				})

				It("populates the uint 32 thing", func() {
					Expect(ts.Uint32Thing).To(Equal(uint32(200000)))
				})

				It("populates the uint 64 thing", func() {
					Expect(ts.Uint64Thing).To(Equal(uint64(200000000)))
				})
			})

			Context("with comma separated strings", func() {
				Context("slice of strings", func() {
					It("populates a slice of strings", func() {
						Expect(ts.StringSliceThing).To(Equal([]string{"one", "two", "three"}))
					})

					Context("with leading and trailing spaces", func() {
						BeforeEach(func() {
							envVars["STRING_SLICE_THING"] = "one , two , three"
						})

						It("populates a slice of strings", func() {
							Expect(ts.StringSliceThing).To(Equal([]string{"one", "two", "three"}))
						})
					})
				})

				Context("slice of ints", func() {
					It("populates a slice of ints", func() {
						Expect(ts.IntSliceThing).To(Equal([]int{1, 2, 3}))
					})
				})
			})
		})

		Context("when load is unsuccessfull", func() {
			Context("when a required environment variable is not given", func() {
				BeforeEach(func() {
					envVars["REQUIRED_THING"] = ""
				})

				It("returns a validation error", func() {
					loadError = envstruct.Load(&ts)

					Expect(loadError).To(MatchError(fmt.Errorf("REQUIRED_THING is required but was empty")))
				})
			})

			Context("with an invalid int", func() {
				BeforeEach(func() {
					envVars["INT_THING"] = "Hello!"
				})

				It("returns an error", func() {
					Expect(envstruct.Load(&ts)).ToNot(Succeed())
				})
			})

			Context("with an invalid uint", func() {
				BeforeEach(func() {
					envVars["UINT_THING"] = "Hello!"
				})

				It("returns an error", func() {
					Expect(envstruct.Load(&ts)).ToNot(Succeed())
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>// Definition for the SOAP structure required for UPnP's SOAP usage.

package soap

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

const (
	soapEncodingStyle = "http://schemas.xmlsoap.org/soap/encoding/"
	soapPrefix        = xml.Header + `<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body>`
	soapSuffix        = `</s:Body></s:Envelope>`
)

type SOAPClient struct {
	EndpointURL url.URL
	HTTPClient  http.Client
}

func NewSOAPClient(endpointURL url.URL) *SOAPClient {
	return &SOAPClient{
		EndpointURL: endpointURL,
	}
}

// PerformSOAPAction makes a SOAP request, with the given action.
// inAction and outAction must both be pointers to structs with string fields
// only.
func (client *SOAPClient) PerformAction(actionNamespace, actionName string, inAction interface{}, outAction interface{}) error {
	requestBytes, err := encodeRequestAction(actionNamespace, actionName, inAction)
	if err != nil {
		return err
	}

	response, err := client.HTTPClient.Do(&http.Request{
		Method: "POST",
		URL:    &client.EndpointURL,
		Header: http.Header{
			"SOAPACTION":   []string{actionNamespace + "#" + actionName},
			"CONTENT-TYPE": []string{"text/xml; charset=\"utf-8\""},
		},
		Body: ioutil.NopCloser(bytes.NewBuffer(requestBytes)),
		// Set ContentLength to avoid chunked encoding - some servers might not support it.
		ContentLength: int64(len(requestBytes)),
	})
	if err != nil {
		return fmt.Errorf("goupnp: error performing SOAP HTTP request: %v", err)
	}
	defer response.Body.Close()
	if response.StatusCode != 200 {
		return fmt.Errorf("goupnp: SOAP request got HTTP %s", response.Status)
	}

	responseEnv := newSOAPEnvelope()
	decoder := xml.NewDecoder(response.Body)
	if err := decoder.Decode(responseEnv); err != nil {
		return fmt.Errorf("goupnp: error decoding response body: %v", err)
	}

	if responseEnv.Body.Fault != nil {
		return responseEnv.Body.Fault
	}

	if outAction != nil {
		if err := xml.Unmarshal(responseEnv.Body.RawAction, outAction); err != nil {
			return fmt.Errorf("goupnp: error unmarshalling out action: %v, %v", err, responseEnv.Body.RawAction)
		}
	}

	return nil
}

// newSOAPAction creates a soapEnvelope with the given action and arguments.
func newSOAPEnvelope() *soapEnvelope {
	return &soapEnvelope{
		EncodingStyle: soapEncodingStyle,
	}
}

// encodeRequestAction is a hacky way to create an encoded SOAP envelope
// containing the given action. Experiments with one router have shown that it
// 500s for requests where the outer default xmlns is set to the SOAP
// namespace, and then reassigning the default namespace within that to the
// service namespace. Hand-coding the outer XML to work-around this.
func encodeRequestAction(actionNamespace, actionName string, inAction interface{}) ([]byte, error) {
	requestBuf := new(bytes.Buffer)
	requestBuf.WriteString(soapPrefix)
	requestBuf.WriteString(`<u:`)
	xml.EscapeText(requestBuf, []byte(actionName))
	requestBuf.WriteString(` xmlns:u="`)
	xml.EscapeText(requestBuf, []byte(actionNamespace))
	requestBuf.WriteString(`">`)
	if inAction != nil {
		if err := encodeRequestArgs(requestBuf, inAction); err != nil {
			return nil, err
		}
	}
	requestBuf.WriteString(`</u:`)
	xml.EscapeText(requestBuf, []byte(actionName))
	requestBuf.WriteString(`>`)
	requestBuf.WriteString(soapSuffix)
	return requestBuf.Bytes(), nil
}

func encodeRequestArgs(w *bytes.Buffer, inAction interface{}) error {
	in := reflect.Indirect(reflect.ValueOf(inAction))
	if in.Kind() != reflect.Struct {
		return fmt.Errorf("goupnp: SOAP inAction is not a struct but of type %v", in.Type())
	}
	enc := xml.NewEncoder(w)
	nFields := in.NumField()
	inType := in.Type()
	for i := 0; i < nFields; i++ {
		field := inType.Field(i)
		argName := field.Name
		if nameOverride := field.Tag.Get("soap"); nameOverride != "" {
			argName = nameOverride
		}
		value := in.Field(i)
		if value.Kind() != reflect.String {
			return fmt.Errorf("goupnp: SOAP arg %q is not of type string, but of type %v", argName, value.Type())
		}
		if err := enc.EncodeElement(value.Interface(), xml.StartElement{xml.Name{"", argName}, nil}); err != nil {
			return fmt.Errorf("goupnp: error encoding SOAP arg %q: %v", argName, err)
		}
	}
	enc.Flush()
	return nil
}

type soapEnvelope struct {
	XMLName       xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
	EncodingStyle string   `xml:"http://schemas.xmlsoap.org/soap/envelope/ encodingStyle,attr"`
	Body          soapBody `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
}

type soapBody struct {
	Fault     *SOAPFaultError `xml:"Fault"`
	RawAction []byte          `xml:",innerxml"`
}

// SOAPFaultError implements error, and contains SOAP fault information.
type SOAPFaultError struct {
	FaultCode   string `xml:"faultcode"`
	FaultString string `xml:"faultstring"`
	Detail      string `xml:"detail"`
}

func (err *SOAPFaultError) Error() string {
	return fmt.Sprintf("SOAP fault: %s", err.FaultString)
}
<commit_msg>soap: quote action names in header<commit_after>// Definition for the SOAP structure required for UPnP's SOAP usage.

package soap

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

const (
	soapEncodingStyle = "http://schemas.xmlsoap.org/soap/encoding/"
	soapPrefix        = xml.Header + `<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body>`
	soapSuffix        = `</s:Body></s:Envelope>`
)

type SOAPClient struct {
	EndpointURL url.URL
	HTTPClient  http.Client
}

func NewSOAPClient(endpointURL url.URL) *SOAPClient {
	return &SOAPClient{
		EndpointURL: endpointURL,
	}
}

// PerformSOAPAction makes a SOAP request, with the given action.
// inAction and outAction must both be pointers to structs with string fields
// only.
func (client *SOAPClient) PerformAction(actionNamespace, actionName string, inAction interface{}, outAction interface{}) error {
	requestBytes, err := encodeRequestAction(actionNamespace, actionName, inAction)
	if err != nil {
		return err
	}

	response, err := client.HTTPClient.Do(&http.Request{
		Method: "POST",
		URL:    &client.EndpointURL,
		Header: http.Header{
			"SOAPACTION":   []string{`"` + actionNamespace + "#" + actionName + `"`},
			"CONTENT-TYPE": []string{"text/xml; charset=\"utf-8\""},
		},
		Body: ioutil.NopCloser(bytes.NewBuffer(requestBytes)),
		// Set ContentLength to avoid chunked encoding - some servers might not support it.
		ContentLength: int64(len(requestBytes)),
	})
	if err != nil {
		return fmt.Errorf("goupnp: error performing SOAP HTTP request: %v", err)
	}
	defer response.Body.Close()
	if response.StatusCode != 200 {
		return fmt.Errorf("goupnp: SOAP request got HTTP %s", response.Status)
	}

	responseEnv := newSOAPEnvelope()
	decoder := xml.NewDecoder(response.Body)
	if err := decoder.Decode(responseEnv); err != nil {
		return fmt.Errorf("goupnp: error decoding response body: %v", err)
	}

	if responseEnv.Body.Fault != nil {
		return responseEnv.Body.Fault
	}

	if outAction != nil {
		if err := xml.Unmarshal(responseEnv.Body.RawAction, outAction); err != nil {
			return fmt.Errorf("goupnp: error unmarshalling out action: %v, %v", err, responseEnv.Body.RawAction)
		}
	}

	return nil
}

// newSOAPAction creates a soapEnvelope with the given action and arguments.
func newSOAPEnvelope() *soapEnvelope {
	return &soapEnvelope{
		EncodingStyle: soapEncodingStyle,
	}
}

// encodeRequestAction is a hacky way to create an encoded SOAP envelope
// containing the given action. Experiments with one router have shown that it
// 500s for requests where the outer default xmlns is set to the SOAP
// namespace, and then reassigning the default namespace within that to the
// service namespace. Hand-coding the outer XML to work-around this.
func encodeRequestAction(actionNamespace, actionName string, inAction interface{}) ([]byte, error) {
	requestBuf := new(bytes.Buffer)
	requestBuf.WriteString(soapPrefix)
	requestBuf.WriteString(`<u:`)
	xml.EscapeText(requestBuf, []byte(actionName))
	requestBuf.WriteString(` xmlns:u="`)
	xml.EscapeText(requestBuf, []byte(actionNamespace))
	requestBuf.WriteString(`">`)
	if inAction != nil {
		if err := encodeRequestArgs(requestBuf, inAction); err != nil {
			return nil, err
		}
	}
	requestBuf.WriteString(`</u:`)
	xml.EscapeText(requestBuf, []byte(actionName))
	requestBuf.WriteString(`>`)
	requestBuf.WriteString(soapSuffix)
	return requestBuf.Bytes(), nil
}

func encodeRequestArgs(w *bytes.Buffer, inAction interface{}) error {
	in := reflect.Indirect(reflect.ValueOf(inAction))
	if in.Kind() != reflect.Struct {
		return fmt.Errorf("goupnp: SOAP inAction is not a struct but of type %v", in.Type())
	}
	enc := xml.NewEncoder(w)
	nFields := in.NumField()
	inType := in.Type()
	for i := 0; i < nFields; i++ {
		field := inType.Field(i)
		argName := field.Name
		if nameOverride := field.Tag.Get("soap"); nameOverride != "" {
			argName = nameOverride
		}
		value := in.Field(i)
		if value.Kind() != reflect.String {
			return fmt.Errorf("goupnp: SOAP arg %q is not of type string, but of type %v", argName, value.Type())
		}
		if err := enc.EncodeElement(value.Interface(), xml.StartElement{xml.Name{"", argName}, nil}); err != nil {
			return fmt.Errorf("goupnp: error encoding SOAP arg %q: %v", argName, err)
		}
	}
	enc.Flush()
	return nil
}

type soapEnvelope struct {
	XMLName       xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
	EncodingStyle string   `xml:"http://schemas.xmlsoap.org/soap/envelope/ encodingStyle,attr"`
	Body          soapBody `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
}

type soapBody struct {
	Fault     *SOAPFaultError `xml:"Fault"`
	RawAction []byte          `xml:",innerxml"`
}

// SOAPFaultError implements error, and contains SOAP fault information.
type SOAPFaultError struct {
	FaultCode   string `xml:"faultcode"`
	FaultString string `xml:"faultstring"`
	Detail      string `xml:"detail"`
}

func (err *SOAPFaultError) Error() string {
	return fmt.Sprintf("SOAP fault: %s", err.FaultString)
}
<|endoftext|>


<commit_before>package etcd

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"

	"github.com/coreos/etcd/config"
	etcdErr "github.com/coreos/etcd/error"
)

// v2client sends various requests using HTTP API.
// It is different from raft communication, and doesn't record anything in the log.
// The argument url is required to contain scheme and host only, and
// there is no trailing slash in it.
// Public functions return "etcd/error".Error intentionally to figure out
// etcd error code easily.
type v2client struct {
	http.Client
}

func newClient(tc *tls.Config) *v2client {
	tr := new(http.Transport)
	tr.TLSClientConfig = tc
	return &v2client{http.Client{Transport: tr}}
}

// CheckVersion returns true when the version check on the server returns 200.
func (c *v2client) CheckVersion(url string, version int) (bool, *etcdErr.Error) {
	resp, err := c.Get(url + fmt.Sprintf("/version/%d/check", version))
	if err != nil {
		return false, clientError(err)
	}

	defer resp.Body.Close()

	return resp.StatusCode == 200, nil
}

// GetVersion fetches the peer version of a cluster.
func (c *v2client) GetVersion(url string) (int, *etcdErr.Error) {
	resp, err := c.Get(url + "/version")
	if err != nil {
		return 0, clientError(err)
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return 0, clientError(err)
	}

	// Parse version number.
	version, err := strconv.Atoi(string(body))
	if err != nil {
		return 0, clientError(err)
	}
	return version, nil
}

func (c *v2client) GetMachines(url string) ([]*machineMessage, *etcdErr.Error) {
	resp, err := c.Get(url + "/v2/admin/machines/")
	if err != nil {
		return nil, clientError(err)
	}

	msgs := new([]*machineMessage)
	if uerr := c.readJSONResponse(resp, msgs); uerr != nil {
		return nil, uerr
	}
	return *msgs, nil
}

func (c *v2client) GetClusterConfig(url string) (*config.ClusterConfig, *etcdErr.Error) {
	resp, err := c.Get(url + "/v2/admin/config")
	if err != nil {
		return nil, clientError(err)
	}

	config := new(config.ClusterConfig)
	if uerr := c.readJSONResponse(resp, config); uerr != nil {
		return nil, uerr
	}
	return config, nil
}

// AddMachine adds machine to the cluster.
// The first return value is the commit index of join command.
func (c *v2client) AddMachine(url string, name string, info *context) *etcdErr.Error {
	b, _ := json.Marshal(info)
	url = url + "/v2/admin/machines/" + name

	log.Printf("Send Join Request to %s", url)
	resp, err := c.put(url, b)
	if err != nil {
		return clientError(err)
	}

	if err := c.readErrorResponse(resp); err != nil {
		return err
	}
	return nil
}

func (c *v2client) readJSONResponse(resp *http.Response, val interface{}) *etcdErr.Error {
	if err := c.readErrorResponse(resp); err != nil {
		return err
	}
	defer resp.Body.Close()
	defer ioutil.ReadAll(resp.Body)

	if err := json.NewDecoder(resp.Body).Decode(val); err != nil {
		log.Printf("Error parsing join response: %v", err)
		return clientError(err)
	}
	return nil
}

func (c *v2client) readErrorResponse(resp *http.Response) *etcdErr.Error {
	if resp.StatusCode == http.StatusOK {
		return nil
	}
	defer resp.Body.Close()
	defer ioutil.ReadAll(resp.Body)

	uerr := &etcdErr.Error{}
	if err := json.NewDecoder(resp.Body).Decode(uerr); err != nil {
		log.Printf("Error parsing response to etcd error: %v", err)
		return clientError(err)
	}
	return uerr
}

// put sends server side PUT request.
// It always follows redirects instead of stopping according to RFC 2616.
func (c *v2client) put(urlStr string, body []byte) (*http.Response, error) {
	return c.doAlwaysFollowingRedirects("PUT", urlStr, body)
}

func (c *v2client) doAlwaysFollowingRedirects(method string, urlStr string, body []byte) (resp *http.Response, err error) {
	var req *http.Request

	for redirect := 0; redirect < 10; redirect++ {
		req, err = http.NewRequest(method, urlStr, bytes.NewBuffer(body))
		if err != nil {
			return
		}

		if resp, err = c.Do(req); err != nil {
			if resp != nil {
				resp.Body.Close()
			}
			return
		}

		if resp.StatusCode == http.StatusMovedPermanently || resp.StatusCode == http.StatusTemporaryRedirect {
			resp.Body.Close()
			if urlStr = resp.Header.Get("Location"); urlStr == "" {
				err = errors.New(fmt.Sprintf("%d response missing Location header", resp.StatusCode))
				return
			}
			continue
		}
		return
	}

	err = errors.New("stopped after 10 redirects")
	return
}

func clientError(err error) *etcdErr.Error {
	return etcdErr.NewError(etcdErr.EcodeClientInternal, err.Error(), 0)
}
<commit_msg>server: refactor client<commit_after>package etcd

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"

	"github.com/coreos/etcd/config"
	etcdErr "github.com/coreos/etcd/error"
)

// v2client sends various requests using HTTP API.
// It is different from raft communication, and doesn't record anything in the log.
// The argument url is required to contain scheme and host only, and
// there is no trailing slash in it.
// Public functions return "etcd/error".Error intentionally to figure out
// etcd error code easily.
type v2client struct {
	http.Client
}

func newClient(tc *tls.Config) *v2client {
	tr := new(http.Transport)
	tr.TLSClientConfig = tc
	return &v2client{http.Client{Transport: tr}}
}

// CheckVersion returns true when the version check on the server returns 200.
func (c *v2client) CheckVersion(url string, version int) (bool, *etcdErr.Error) {
	resp, err := c.Get(url + fmt.Sprintf("/version/%d/check", version))
	if err != nil {
		return false, clientError(err)
	}

	defer resp.Body.Close()

	return resp.StatusCode == 200, nil
}

// GetVersion fetches the peer version of a cluster.
func (c *v2client) GetVersion(url string) (int, *etcdErr.Error) {
	resp, err := c.Get(url + "/version")
	if err != nil {
		return 0, clientError(err)
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return 0, clientError(err)
	}

	// Parse version number.
	version, err := strconv.Atoi(string(body))
	if err != nil {
		return 0, clientError(err)
	}
	return version, nil
}

func (c *v2client) GetMachines(url string) ([]*machineMessage, *etcdErr.Error) {
	resp, err := c.Get(url + "/v2/admin/machines/")
	if err != nil {
		return nil, clientError(err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, c.readErrorBody(resp.Body)
	}

	msgs := new([]*machineMessage)
	if uerr := c.readJSONBody(resp.Body, msgs); uerr != nil {
		return nil, uerr
	}
	return *msgs, nil
}

func (c *v2client) GetClusterConfig(url string) (*config.ClusterConfig, *etcdErr.Error) {
	resp, err := c.Get(url + "/v2/admin/config")
	if err != nil {
		return nil, clientError(err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, c.readErrorBody(resp.Body)
	}

	config := new(config.ClusterConfig)
	if uerr := c.readJSONBody(resp.Body, config); uerr != nil {
		return nil, uerr
	}
	return config, nil
}

// AddMachine adds machine to the cluster.
// The first return value is the commit index of join command.
func (c *v2client) AddMachine(url string, name string, info *context) *etcdErr.Error {
	b, _ := json.Marshal(info)
	url = url + "/v2/admin/machines/" + name

	log.Printf("Send Join Request to %s", url)
	resp, err := c.put(url, b)
	if err != nil {
		return clientError(err)
	}
	if resp.StatusCode != http.StatusOK {
		return c.readErrorBody(resp.Body)
	}
	c.readBody(resp.Body)
	return nil
}

func (c *v2client) readErrorBody(body io.ReadCloser) *etcdErr.Error {
	b, err := c.readBody(body)
	if err != nil {
		return clientError(err)
	}
	uerr := &etcdErr.Error{}
	if err := json.Unmarshal(b, uerr); err != nil {
		str := strings.TrimSpace(string(b))
		return etcdErr.NewError(etcdErr.EcodeClientInternal, str, 0)
	}
	return nil
}

func (c *v2client) readJSONBody(body io.ReadCloser, val interface{}) *etcdErr.Error {
	if err := json.NewDecoder(body).Decode(val); err != nil {
		log.Printf("Error parsing join response: %v", err)
		return clientError(err)
	}
	c.readBody(body)
	return nil
}

func (c *v2client) readBody(body io.ReadCloser) ([]byte, error) {
	b, err := ioutil.ReadAll(body)
	body.Close()
	return b, err
}

// put sends server side PUT request.
// It always follows redirects instead of stopping according to RFC 2616.
func (c *v2client) put(urlStr string, body []byte) (*http.Response, error) {
	return c.doAlwaysFollowingRedirects("PUT", urlStr, body)
}

func (c *v2client) doAlwaysFollowingRedirects(method string, urlStr string, body []byte) (resp *http.Response, err error) {
	var req *http.Request

	for redirect := 0; redirect < 10; redirect++ {
		req, err = http.NewRequest(method, urlStr, bytes.NewBuffer(body))
		if err != nil {
			return
		}

		if resp, err = c.Do(req); err != nil {
			if resp != nil {
				resp.Body.Close()
			}
			return
		}

		if resp.StatusCode == http.StatusMovedPermanently || resp.StatusCode == http.StatusTemporaryRedirect {
			resp.Body.Close()
			if urlStr = resp.Header.Get("Location"); urlStr == "" {
				err = errors.New(fmt.Sprintf("%d response missing Location header", resp.StatusCode))
				return
			}
			continue
		}
		return
	}

	err = errors.New("stopped after 10 redirects")
	return
}

func clientError(err error) *etcdErr.Error {
	return etcdErr.NewError(etcdErr.EcodeClientInternal, err.Error(), 0)
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

type PawnEntry struct {
	hash       uint64 	// Pawn hash key.
	score      Score 	// Static score for the given pawn structure.
	king       [2]int 	// King square for both sides.
	cover      [2]Score 	// King cover penalties for both sides.
	passers    [2]Bitmask 	// Passed pawn bitmasks for both sides.
}

type PawnCache [8192]PawnEntry

func (e *Evaluation) analyzePawns() {
	key := e.position.pawnHash

	// Since pawn hash is fairly small we can use much faster 32-bit index.
	index := uint32(key) % uint32(len(game.pawnCache))
	e.pawns = &game.pawnCache[index]

	// Bypass pawns cache if evaluation tracing is enabled.
	if e.pawns.hash != key || engine.trace {
		white, black := e.pawnStructure(White), e.pawnStructure(Black)
		white.apply(weights[1]); black.apply(weights[1]) // <-- Pawn structure weight.
		e.pawns.score.clear().add(white).subtract(black)
		e.pawns.hash = key

		// Force full king shelter evaluation since any legit king square
		// will be viewed as if the king has moved.
		e.pawns.king[White], e.pawns.king[Black] = -1, -1

		if engine.trace {
			e.checkpoint(`Pawns`, Total{white, black})
		}
	}

	e.score.add(e.pawns.score)
}

func (e *Evaluation) analyzePassers() {
	var white, black Score

	if engine.trace {
		defer func() {
			e.checkpoint(`Passers`, Total{white, black})
		}()
	}

	white, black = e.pawnPassers(White), e.pawnPassers(Black)
	white.apply(weights[2]); black.apply(weights[2]) // <-- Passed pawns weight.
	e.score.add(white).subtract(black)
}

// Calculates extra bonus and penalty based on pawn structure. Specifically,
// a bonus is awarded for passed pawns, and penalty applied for isolated and
// doubled pawns.
func (e *Evaluation) pawnStructure(color int) (score Score) {
	hisPawns := e.position.outposts[pawn(color)]
	herPawns := e.position.outposts[pawn(color^1)]
	e.pawns.passers[color] = 0

	// Encourage center pawn moves in the opening.
	pawns := hisPawns
	if e.material.phase > 240 && (pawns & maskCenter) == 0 {
		score.midgame += bonusPawnCenter[(pawns & maskCenter).count()]
	}

	for pawns != 0 {
		square := pawns.pop()
		row, col := Coordinate(square)

		// Penalty if the pawn is isolated, i.e. has no friendly pawns
		// on adjacent files. The penalty goes up if isolated pawn is
		// exposed on semi-open file.
		isolated := (maskIsolated[col] & hisPawns == 0)
		exposed := (maskInFront[color][square] & herPawns == 0)
		if isolated {
			if !exposed {
				score.subtract(penaltyIsolatedPawn[col])
			} else {
				score.subtract(penaltyWeakIsolatedPawn[col])
			}
		}

		// Penalty if the pawn is doubled, i.e. there is another friendly
		// pawn in front of us. The penalty goes up if doubled pawns are
		// isolated.
		doubled := (maskInFront[color][square] & hisPawns != 0)
		if doubled {
			score.subtract(penaltyDoubledPawn[col])
		}

		// Bonus if the pawn is supported by friendly pawn(s) on the same
		// or previous ranks.
		supported := (maskIsolated[col] & (maskRank[row] | maskRank[row].pushed(color^1)) & hisPawns != 0)
		if supported {
			flip := Flip(color, square)
			score.add(Score{bonusSupportedPawn[flip], bonusSupportedPawn[flip]})
		}

		// The pawn is passed if a) there are no enemy pawns in the same
		// and adjacent columns; and b) there are no same color pawns in
		// front of us.
		passed := (maskPassed[color][square] & herPawns == 0 && !doubled)
		if passed {
			e.pawns.passers[color] |= bit[square]
		}

		// Penalty if the pawn is backward.
		if (!passed && !supported && !isolated) {

			// Backward pawn should not be attacking enemy pawns.
			if pawnMoves[color][square] & herPawns == 0 {

				// Backward pawn should not have friendly pawns behind.
				if maskPassed[color^1][square] & maskIsolated[col] & hisPawns == 0 {

					// Backward pawn should face enemy pawns on the next two ranks
					// preventing its advance.
					enemy := pawnMoves[color][square].pushed(color)
					if (enemy | enemy.pushed(color)) & herPawns != 0 {
						if !exposed {
							score.subtract(penaltyBackwardPawn[col])
						} else {
							score.subtract(penaltyWeakBackwardPawn[col])
						}
					}
				}
			}
		}

		// TODO: Bonus if the pawn has good chance to become a passed pawn.
	}

	return
}

func (e *Evaluation) pawnPassers(color int) (score Score) {
	p := e.position

	pawns := e.pawns.passers[color]
	for pawns != 0 {
		square := pawns.pop()
		row := RelRow(square, color)
		bonus := bonusPassedPawn[row]

		if row > A2H2 {
			extra := extraPassedPawn[row]
			nextSquare := square + eight[color]

			// Adjust endgame bonus based on how close the kings are from the
			// step forward square.
			bonus.endgame += (distance[p.king[color^1]][nextSquare] * 5 - distance[p.king[color]][nextSquare] * 2) * extra

			// Check if the pawn can step forward.
			if p.board.isClear(nextSquare) {
				boost := 0

				// Assume all squares in front of the pawn are under attack.
				attacked := maskInFront[color][square]
				protected := attacked & e.attacks[color]

				// Boost the bonus if squares in front of the pawn are protected.
				if protected == attacked {
					boost += 6 // All squares.
				} else if protected.isSet(nextSquare) {
					boost += 4 // Next square only.
				}

				// Check who is attacking the squares in front of the pawn including
				// queen and rook x-ray attacks from behind.
				enemy := maskInFront[color^1][square] & (p.outposts[queen(color^1)] | p.outposts[rook(color^1)])
				if enemy == 0 || enemy & p.rookMoves(square) == 0 {

					// Since nobody attacks the pawn from behind adjust the attacked
					// bitmask to only include squares attacked or occupied by the enemy.
					attacked &= (e.attacks[color^1] | p.outposts[color^1])
				}

				// Boost the bonus if passed pawn is free to advance to the 8th rank
				// or at least safely step forward.
				if attacked == 0 {
					boost += 15 // Remaining squares are not under attack.
				} else if attacked.isClear(nextSquare) {
					boost += 9  // Next square is not under attack.
				}

				if boost > 0 {
					bonus.adjust(extra * boost)
				}
			}
		}
		score.add(bonus)
	}

	// Penalty for blocked pawns.
	blocked := (p.outposts[pawn(color)].pushed(color) & p.board).count()
	score.subtract(pawnBlocked.times(blocked))

	return
}

<commit_msg>Semi-passed pawn evaluation<commit_after>// Copyright (c) 2013-2014 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

type PawnEntry struct {
	hash       uint64 	// Pawn hash key.
	score      Score 	// Static score for the given pawn structure.
	king       [2]int 	// King square for both sides.
	cover      [2]Score 	// King cover penalties for both sides.
	passers    [2]Bitmask 	// Passed pawn bitmasks for both sides.
}

type PawnCache [8192]PawnEntry

func (e *Evaluation) analyzePawns() {
	key := e.position.pawnHash

	// Since pawn hash is fairly small we can use much faster 32-bit index.
	index := uint32(key) % uint32(len(game.pawnCache))
	e.pawns = &game.pawnCache[index]

	// Bypass pawns cache if evaluation tracing is enabled.
	if e.pawns.hash != key || engine.trace {
		white, black := e.pawnStructure(White), e.pawnStructure(Black)
		white.apply(weights[1]); black.apply(weights[1]) // <-- Pawn structure weight.
		e.pawns.score.clear().add(white).subtract(black)
		e.pawns.hash = key

		// Force full king shelter evaluation since any legit king square
		// will be viewed as if the king has moved.
		e.pawns.king[White], e.pawns.king[Black] = -1, -1

		if engine.trace {
			e.checkpoint(`Pawns`, Total{white, black})
		}
	}

	e.score.add(e.pawns.score)
}

func (e *Evaluation) analyzePassers() {
	var white, black Score

	if engine.trace {
		defer func() {
			e.checkpoint(`Passers`, Total{white, black})
		}()
	}

	white, black = e.pawnPassers(White), e.pawnPassers(Black)
	white.apply(weights[2]); black.apply(weights[2]) // <-- Passed pawns weight.
	e.score.add(white).subtract(black)
}

// Calculates extra bonus and penalty based on pawn structure. Specifically,
// a bonus is awarded for passed pawns, and penalty applied for isolated and
// doubled pawns.
func (e *Evaluation) pawnStructure(color int) (score Score) {
	hisPawns := e.position.outposts[pawn(color)]
	herPawns := e.position.outposts[pawn(color^1)]
	e.pawns.passers[color] = 0

	// Encourage center pawn moves in the opening.
	pawns := hisPawns
	if e.material.phase > 255 && (pawns & maskCenter) == 0 {
		score.midgame += bonusPawnCenter[(pawns & maskCenter).count()]
	}

	for pawns != 0 {
		square := pawns.pop()
		row, col := Coordinate(square)

		// Penalty if the pawn is isolated, i.e. has no friendly pawns
		// on adjacent files. The penalty goes up if isolated pawn is
		// exposed on semi-open file.
		isolated := (maskIsolated[col] & hisPawns == 0)
		exposed := (maskInFront[color][square] & herPawns == 0)
		if isolated {
			if !exposed {
				score.subtract(penaltyIsolatedPawn[col])
			} else {
				score.subtract(penaltyWeakIsolatedPawn[col])
			}
		}

		// Penalty if the pawn is doubled, i.e. there is another friendly
		// pawn in front of us. The penalty goes up if doubled pawns are
		// isolated.
		doubled := (maskInFront[color][square] & hisPawns != 0)
		if doubled {
			score.subtract(penaltyDoubledPawn[col])
		}

		// Bonus if the pawn is supported by friendly pawn(s) on the same
		// or previous ranks.
		supported := (maskIsolated[col] & (maskRank[row] | maskRank[row].pushed(color^1)) & hisPawns != 0)
		if supported {
			flip := Flip(color, square)
			score.add(Score{bonusSupportedPawn[flip], bonusSupportedPawn[flip]})
		}

		// The pawn is passed if a) there are no enemy pawns in the same
		// and adjacent columns; and b) there are no same color pawns in
		// front of us.
		passed := (maskPassed[color][square] & herPawns == 0 && !doubled)
		if passed {
			e.pawns.passers[color] |= bit[square]
		}

		// Penalty if the pawn is backward.
		backward := false
		if (!passed && !supported && !isolated) {

			// Backward pawn should not be attacking enemy pawns.
			if pawnMoves[color][square] & herPawns == 0 {

				// Backward pawn should not have friendly pawns behind.
				if maskPassed[color^1][square] & maskIsolated[col] & hisPawns == 0 {

					// Backward pawn should face enemy pawns on the next two ranks
					// preventing its advance.
					enemy := pawnMoves[color][square].pushed(color)
					if (enemy | enemy.pushed(color)) & herPawns != 0 {
						backward = true
						if !exposed {
							score.subtract(penaltyBackwardPawn[col])
						} else {
							score.subtract(penaltyWeakBackwardPawn[col])
						}
					}
				}
			}
		}

		// TODO: Bonus if the pawn has good chance to become a passed pawn.
		if exposed && supported && !passed && !backward {
			his := maskPassed[color^1][square + eight[color]] & maskIsolated[col] & hisPawns
			her := maskPassed[color][square] & maskIsolated[col] & herPawns
			if his.count() >= her.count() {
				score.add(bonusSemiPassedPawn[RelRow(square, color)])
			}
		}
	}

	// Penalty for blocked pawns.
	blocked := (hisPawns.pushed(color) & e.position.board).count()
	score.subtract(pawnBlocked.times(blocked))

	return
}

func (e *Evaluation) pawnPassers(color int) (score Score) {
	p := e.position

	pawns := e.pawns.passers[color]
	for pawns != 0 {
		square := pawns.pop()
		row := RelRow(square, color)
		bonus := bonusPassedPawn[row]

		if row > A2H2 {
			extra := extraPassedPawn[row]
			nextSquare := square + eight[color]

			// Adjust endgame bonus based on how close the kings are from the
			// step forward square.
			bonus.endgame += (distance[p.king[color^1]][nextSquare] * 5 - distance[p.king[color]][nextSquare] * 2) * extra

			// Check if the pawn can step forward.
			if p.board.isClear(nextSquare) {
				boost := 0

				// Assume all squares in front of the pawn are under attack.
				attacked := maskInFront[color][square]
				protected := attacked & e.attacks[color]

				// Boost the bonus if squares in front of the pawn are protected.
				if protected == attacked {
					boost += 6 // All squares.
				} else if protected.isSet(nextSquare) {
					boost += 4 // Next square only.
				}

				// Check who is attacking the squares in front of the pawn including
				// queen and rook x-ray attacks from behind.
				enemy := maskInFront[color^1][square] & (p.outposts[queen(color^1)] | p.outposts[rook(color^1)])
				if enemy == 0 || enemy & p.rookMoves(square) == 0 {

					// Since nobody attacks the pawn from behind adjust the attacked
					// bitmask to only include squares attacked or occupied by the enemy.
					attacked &= (e.attacks[color^1] | p.outposts[color^1])
				}

				// Boost the bonus if passed pawn is free to advance to the 8th rank
				// or at least safely step forward.
				if attacked == 0 {
					boost += 15 // Remaining squares are not under attack.
				} else if attacked.isClear(nextSquare) {
					boost += 9  // Next square is not under attack.
				}

				if boost > 0 {
					bonus.adjust(extra * boost)
				}
			}
		}
		score.add(bonus)
	}

	return
}

<|endoftext|>


<commit_before>// Package sqlboiler has types and methods useful for generating code that
// acts as a fully dynamic ORM might.
package main

import (
	"encoding/json"
	"fmt"
	"go/build"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"text/template"

	"github.com/pkg/errors"
	"github.com/vattle/sqlboiler/bdb"
	"github.com/vattle/sqlboiler/bdb/drivers"
	"github.com/vattle/sqlboiler/boil"
)

const (
	templatesDirectory          = "templates"
	templatesSingletonDirectory = "templates/singleton"

	templatesTestDirectory          = "templates_test"
	templatesSingletonTestDirectory = "templates_test/singleton"

	templatesTestMainDirectory = "templates_test/main_test"
)

// State holds the global data needed by most pieces to run
type State struct {
	Config *Config

	Driver  bdb.Interface
	Tables  []bdb.Table
	Dialect boil.Dialect

	Templates              *templateList
	TestTemplates          *templateList
	SingletonTemplates     *templateList
	SingletonTestTemplates *templateList

	TestMainTemplate *template.Template
}

// New creates a new state based off of the config
func New(config *Config) (*State, error) {
	s := &State{
		Config: config,
	}

	err := s.initDriver(config.DriverName)
	if err != nil {
		return nil, err
	}

	// Connect to the driver database
	if err = s.Driver.Open(); err != nil {
		return nil, errors.Wrap(err, "unable to connect to the database")
	}

	err = s.initTables(config.Schema, config.WhitelistTables, config.BlacklistTables)
	if err != nil {
		return nil, errors.Wrap(err, "unable to initialize tables")
	}

	if s.Config.Debug {
		b, err := json.Marshal(s.Tables)
		if err != nil {
			return nil, errors.Wrap(err, "unable to json marshal tables")
		}
		boil.DebugWriter.Write(b)
		fmt.Fprintln(boil.DebugWriter)
	}

	err = s.initOutFolder()
	if err != nil {
		return nil, errors.Wrap(err, "unable to initialize the output folder")
	}

	err = s.initTemplates()
	if err != nil {
		return nil, errors.Wrap(err, "unable to initialize templates")
	}

	err = s.initTags(config.Tags)
	if err != nil {
		return nil, errors.Wrap(err, "unable to initialize struct tags")
	}

	return s, nil
}

// Run executes the sqlboiler templates and outputs them to files based on the
// state given.
func (s *State) Run(includeTests bool) error {
	singletonData := &templateData{
		Tables:           s.Tables,
		Schema:           s.Config.Schema,
		DriverName:       s.Config.DriverName,
		UseLastInsertID:  s.Driver.UseLastInsertID(),
		PkgName:          s.Config.PkgName,
		NoHooks:          s.Config.NoHooks,
		NoAutoTimestamps: s.Config.NoAutoTimestamps,
		Dialect:          s.Dialect,

		StringFuncs: templateStringMappers,
	}

	if err := generateSingletonOutput(s, singletonData); err != nil {
		return errors.Wrap(err, "singleton template output")
	}

	if !s.Config.NoTests && includeTests {
		if err := generateTestMainOutput(s, singletonData); err != nil {
			return errors.Wrap(err, "unable to generate TestMain output")
		}

		if err := generateSingletonTestOutput(s, singletonData); err != nil {
			return errors.Wrap(err, "unable to generate singleton test template output")
		}
	}

	for _, table := range s.Tables {
		if table.IsJoinTable {
			continue
		}

		data := &templateData{
			Tables:           s.Tables,
			Table:            table,
			Schema:           s.Config.Schema,
			DriverName:       s.Config.DriverName,
			UseLastInsertID:  s.Driver.UseLastInsertID(),
			PkgName:          s.Config.PkgName,
			NoHooks:          s.Config.NoHooks,
			NoAutoTimestamps: s.Config.NoAutoTimestamps,
			Tags:             s.Config.Tags,
			Dialect:          s.Dialect,

			StringFuncs: templateStringMappers,
		}

		// Generate the regular templates
		if err := generateOutput(s, data); err != nil {
			return errors.Wrap(err, "unable to generate output")
		}

		// Generate the test templates
		if !s.Config.NoTests && includeTests {
			if err := generateTestOutput(s, data); err != nil {
				return errors.Wrap(err, "unable to generate test output")
			}
		}
	}

	return nil
}

// Cleanup closes any resources that must be closed
func (s *State) Cleanup() error {
	s.Driver.Close()
	return nil
}

// initTemplates loads all template folders into the state object.
func (s *State) initTemplates() error {
	var err error

	basePath, err := getBasePath(s.Config.BaseDir)
	if err != nil {
		return err
	}

	s.Templates, err = loadTemplates(filepath.Join(basePath, templatesDirectory))
	if err != nil {
		return err
	}

	s.SingletonTemplates, err = loadTemplates(filepath.Join(basePath, templatesSingletonDirectory))
	if err != nil {
		return err
	}

	if !s.Config.NoTests {
		s.TestTemplates, err = loadTemplates(filepath.Join(basePath, templatesTestDirectory))
		if err != nil {
			return err
		}

		s.SingletonTestTemplates, err = loadTemplates(filepath.Join(basePath, templatesSingletonTestDirectory))
		if err != nil {
			return err
		}

		s.TestMainTemplate, err = loadTemplate(filepath.Join(basePath, templatesTestMainDirectory), s.Config.DriverName+"_main.tpl")
		if err != nil {
			return err
		}
	}

	return nil
}

var basePackage = "github.com/vattle/sqlboiler"

func getBasePath(baseDirConfig string) (string, error) {
	if len(baseDirConfig) > 0 {
		return baseDirConfig, nil
	}

	p, _ := build.Default.Import(basePackage, "", build.FindOnly)
	if p != nil && len(p.Dir) > 0 {
		return p.Dir, nil
	}

	return os.Getwd()
}

// initDriver attempts to set the state Interface based off the passed in
// driver flag value. If an invalid flag string is provided an error is returned.
func (s *State) initDriver(driverName string) error {
	// Create a driver based off driver flag
	switch driverName {
	case "postgres":
		s.Driver = drivers.NewPostgresDriver(
			s.Config.Postgres.User,
			s.Config.Postgres.Pass,
			s.Config.Postgres.DBName,
			s.Config.Postgres.Host,
			s.Config.Postgres.Port,
			s.Config.Postgres.SSLMode,
		)
	case "mysql":
		s.Driver = drivers.NewMySQLDriver(
			s.Config.MySQL.User,
			s.Config.MySQL.Pass,
			s.Config.MySQL.DBName,
			s.Config.MySQL.Host,
			s.Config.MySQL.Port,
			s.Config.MySQL.SSLMode,
		)
	case "mock":
		s.Driver = &drivers.MockDriver{}
	}

	if s.Driver == nil {
		return errors.New("An invalid driver name was provided")
	}

	s.Dialect.LQ = s.Driver.LeftQuote()
	s.Dialect.RQ = s.Driver.RightQuote()
	s.Dialect.IndexPlaceholders = s.Driver.IndexPlaceholders()

	return nil
}

// initTables retrieves all "public" schema table names from the database.
func (s *State) initTables(schema string, whitelist, blacklist []string) error {
	var err error
	s.Tables, err = bdb.Tables(s.Driver, schema, whitelist, blacklist)
	if err != nil {
		return errors.Wrap(err, "unable to fetch table data")
	}

	if len(s.Tables) == 0 {
		return errors.New("no tables found in database")
	}

	if err := checkPKeys(s.Tables); err != nil {
		return err
	}

	return nil
}

// Tags must be in a format like: json, xml, etc.
var rgxValidTag = regexp.MustCompile(`[a-zA-Z_\.]+`)

// initTags removes duplicate tags and validates the format
// of all user tags are simple strings without quotes: [a-zA-Z_\.]+
func (s *State) initTags(tags []string) error {
	s.Config.Tags = removeDuplicates(s.Config.Tags)
	for _, v := range s.Config.Tags {
		if !rgxValidTag.MatchString(v) {
			return errors.New("Invalid tag format %q supplied, only specify name, eg: xml")
		}
	}

	return nil
}

// initOutFolder creates the folder that will hold the generated output.
func (s *State) initOutFolder() error {
	return os.MkdirAll(s.Config.OutFolder, os.ModePerm)
}

// checkPKeys ensures every table has a primary key column
func checkPKeys(tables []bdb.Table) error {
	var missingPkey []string
	for _, t := range tables {
		if t.PKey == nil {
			missingPkey = append(missingPkey, t.Name)
		}
	}

	if len(missingPkey) != 0 {
		return errors.Errorf("primary key missing in tables (%s)", strings.Join(missingPkey, ", "))
	}

	return nil
}
<commit_msg>Fix bug in debug output<commit_after>// Package sqlboiler has types and methods useful for generating code that
// acts as a fully dynamic ORM might.
package main

import (
	"encoding/json"
	"fmt"
	"go/build"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"text/template"

	"github.com/pkg/errors"
	"github.com/vattle/sqlboiler/bdb"
	"github.com/vattle/sqlboiler/bdb/drivers"
	"github.com/vattle/sqlboiler/boil"
)

const (
	templatesDirectory          = "templates"
	templatesSingletonDirectory = "templates/singleton"

	templatesTestDirectory          = "templates_test"
	templatesSingletonTestDirectory = "templates_test/singleton"

	templatesTestMainDirectory = "templates_test/main_test"
)

// State holds the global data needed by most pieces to run
type State struct {
	Config *Config

	Driver  bdb.Interface
	Tables  []bdb.Table
	Dialect boil.Dialect

	Templates              *templateList
	TestTemplates          *templateList
	SingletonTemplates     *templateList
	SingletonTestTemplates *templateList

	TestMainTemplate *template.Template
}

// New creates a new state based off of the config
func New(config *Config) (*State, error) {
	s := &State{
		Config: config,
	}

	err := s.initDriver(config.DriverName)
	if err != nil {
		return nil, err
	}

	// Connect to the driver database
	if err = s.Driver.Open(); err != nil {
		return nil, errors.Wrap(err, "unable to connect to the database")
	}

	err = s.initTables(config.Schema, config.WhitelistTables, config.BlacklistTables)
	if err != nil {
		return nil, errors.Wrap(err, "unable to initialize tables")
	}

	if s.Config.Debug {
		b, err := json.Marshal(s.Tables)
		if err != nil {
			return nil, errors.Wrap(err, "unable to json marshal tables")
		}
		fmt.Printf("%s\n", b)
	}

	err = s.initOutFolder()
	if err != nil {
		return nil, errors.Wrap(err, "unable to initialize the output folder")
	}

	err = s.initTemplates()
	if err != nil {
		return nil, errors.Wrap(err, "unable to initialize templates")
	}

	err = s.initTags(config.Tags)
	if err != nil {
		return nil, errors.Wrap(err, "unable to initialize struct tags")
	}

	return s, nil
}

// Run executes the sqlboiler templates and outputs them to files based on the
// state given.
func (s *State) Run(includeTests bool) error {
	singletonData := &templateData{
		Tables:           s.Tables,
		Schema:           s.Config.Schema,
		DriverName:       s.Config.DriverName,
		UseLastInsertID:  s.Driver.UseLastInsertID(),
		PkgName:          s.Config.PkgName,
		NoHooks:          s.Config.NoHooks,
		NoAutoTimestamps: s.Config.NoAutoTimestamps,
		Dialect:          s.Dialect,

		StringFuncs: templateStringMappers,
	}

	if err := generateSingletonOutput(s, singletonData); err != nil {
		return errors.Wrap(err, "singleton template output")
	}

	if !s.Config.NoTests && includeTests {
		if err := generateTestMainOutput(s, singletonData); err != nil {
			return errors.Wrap(err, "unable to generate TestMain output")
		}

		if err := generateSingletonTestOutput(s, singletonData); err != nil {
			return errors.Wrap(err, "unable to generate singleton test template output")
		}
	}

	for _, table := range s.Tables {
		if table.IsJoinTable {
			continue
		}

		data := &templateData{
			Tables:           s.Tables,
			Table:            table,
			Schema:           s.Config.Schema,
			DriverName:       s.Config.DriverName,
			UseLastInsertID:  s.Driver.UseLastInsertID(),
			PkgName:          s.Config.PkgName,
			NoHooks:          s.Config.NoHooks,
			NoAutoTimestamps: s.Config.NoAutoTimestamps,
			Tags:             s.Config.Tags,
			Dialect:          s.Dialect,

			StringFuncs: templateStringMappers,
		}

		// Generate the regular templates
		if err := generateOutput(s, data); err != nil {
			return errors.Wrap(err, "unable to generate output")
		}

		// Generate the test templates
		if !s.Config.NoTests && includeTests {
			if err := generateTestOutput(s, data); err != nil {
				return errors.Wrap(err, "unable to generate test output")
			}
		}
	}

	return nil
}

// Cleanup closes any resources that must be closed
func (s *State) Cleanup() error {
	s.Driver.Close()
	return nil
}

// initTemplates loads all template folders into the state object.
func (s *State) initTemplates() error {
	var err error

	basePath, err := getBasePath(s.Config.BaseDir)
	if err != nil {
		return err
	}

	s.Templates, err = loadTemplates(filepath.Join(basePath, templatesDirectory))
	if err != nil {
		return err
	}

	s.SingletonTemplates, err = loadTemplates(filepath.Join(basePath, templatesSingletonDirectory))
	if err != nil {
		return err
	}

	if !s.Config.NoTests {
		s.TestTemplates, err = loadTemplates(filepath.Join(basePath, templatesTestDirectory))
		if err != nil {
			return err
		}

		s.SingletonTestTemplates, err = loadTemplates(filepath.Join(basePath, templatesSingletonTestDirectory))
		if err != nil {
			return err
		}

		s.TestMainTemplate, err = loadTemplate(filepath.Join(basePath, templatesTestMainDirectory), s.Config.DriverName+"_main.tpl")
		if err != nil {
			return err
		}
	}

	return nil
}

var basePackage = "github.com/vattle/sqlboiler"

func getBasePath(baseDirConfig string) (string, error) {
	if len(baseDirConfig) > 0 {
		return baseDirConfig, nil
	}

	p, _ := build.Default.Import(basePackage, "", build.FindOnly)
	if p != nil && len(p.Dir) > 0 {
		return p.Dir, nil
	}

	return os.Getwd()
}

// initDriver attempts to set the state Interface based off the passed in
// driver flag value. If an invalid flag string is provided an error is returned.
func (s *State) initDriver(driverName string) error {
	// Create a driver based off driver flag
	switch driverName {
	case "postgres":
		s.Driver = drivers.NewPostgresDriver(
			s.Config.Postgres.User,
			s.Config.Postgres.Pass,
			s.Config.Postgres.DBName,
			s.Config.Postgres.Host,
			s.Config.Postgres.Port,
			s.Config.Postgres.SSLMode,
		)
	case "mysql":
		s.Driver = drivers.NewMySQLDriver(
			s.Config.MySQL.User,
			s.Config.MySQL.Pass,
			s.Config.MySQL.DBName,
			s.Config.MySQL.Host,
			s.Config.MySQL.Port,
			s.Config.MySQL.SSLMode,
		)
	case "mock":
		s.Driver = &drivers.MockDriver{}
	}

	if s.Driver == nil {
		return errors.New("An invalid driver name was provided")
	}

	s.Dialect.LQ = s.Driver.LeftQuote()
	s.Dialect.RQ = s.Driver.RightQuote()
	s.Dialect.IndexPlaceholders = s.Driver.IndexPlaceholders()

	return nil
}

// initTables retrieves all "public" schema table names from the database.
func (s *State) initTables(schema string, whitelist, blacklist []string) error {
	var err error
	s.Tables, err = bdb.Tables(s.Driver, schema, whitelist, blacklist)
	if err != nil {
		return errors.Wrap(err, "unable to fetch table data")
	}

	if len(s.Tables) == 0 {
		return errors.New("no tables found in database")
	}

	if err := checkPKeys(s.Tables); err != nil {
		return err
	}

	return nil
}

// Tags must be in a format like: json, xml, etc.
var rgxValidTag = regexp.MustCompile(`[a-zA-Z_\.]+`)

// initTags removes duplicate tags and validates the format
// of all user tags are simple strings without quotes: [a-zA-Z_\.]+
func (s *State) initTags(tags []string) error {
	s.Config.Tags = removeDuplicates(s.Config.Tags)
	for _, v := range s.Config.Tags {
		if !rgxValidTag.MatchString(v) {
			return errors.New("Invalid tag format %q supplied, only specify name, eg: xml")
		}
	}

	return nil
}

// initOutFolder creates the folder that will hold the generated output.
func (s *State) initOutFolder() error {
	return os.MkdirAll(s.Config.OutFolder, os.ModePerm)
}

// checkPKeys ensures every table has a primary key column
func checkPKeys(tables []bdb.Table) error {
	var missingPkey []string
	for _, t := range tables {
		if t.PKey == nil {
			missingPkey = append(missingPkey, t.Name)
		}
	}

	if len(missingPkey) != 0 {
		return errors.Errorf("primary key missing in tables (%s)", strings.Join(missingPkey, ", "))
	}

	return nil
}
<|endoftext|>


<commit_before>package postgresql

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"net/url"
	"strings"

	"github.com/amonapp/amonagent/logging"
	"github.com/amonapp/amonagent/plugins"
	"github.com/mitchellh/mapstructure"
	// Postgres Driver
	_ "github.com/lib/pq"
)

var pluginLogger = logging.GetLogger("amonagent.postgresql")

// Counters - XXX
var Counters = map[string]string{
	"xact_commit":   "xact.commits",
	"xact_rollback": "xact.rollbacks",
	"blks_read":     "performance.disk_read",
	"blks_hit":      "performance.buffer_hit",
	"tup_returned":  "rows.returned",
	"tup_fetched":   "rows.fetched",
	"tup_inserted":  "rows.inserted",
	"tup_updated":   "rows.updated",
	"tup_deleted":   "rows.deleted",
}

// Gauges - XXX
var Gauges = map[string]string{
	"numbackends": "connections",
}

// DatabaseStatsSQL - XXX
var DatabaseStatsSQL = `SELECT %s FROM pg_stat_database WHERE datname IN ('%s')`

// MissingIndexesSQL - XXX
var MissingIndexesSQL = `SELECT
		  relname AS table,
		  CASE idx_scan
			WHEN 0 THEN 'Insufficient data'
			ELSE (100 * idx_scan / (seq_scan + idx_scan))::text
		  END percent_of_times_index_used,
		  n_live_tup rows_in_table
		FROM
		  pg_stat_user_tables
		WHERE
		  idx_scan > 0
		  AND (100 * idx_scan / (seq_scan + idx_scan)) < 95
		  AND n_live_tup >= 10000
		ORDER BY
		  n_live_tup DESC,
		  relname ASC
`

// SlowQueriesSQL - XXX
var SlowQueriesSQL = `SELECT * FROM
		(SELECT
				calls,
				round(total_time :: NUMERIC, 1) AS total,
				round(
					(total_time / calls) :: NUMERIC,
					3
				) AS per_call,
				regexp_replace(query, '[ \t\n]+', ' ', 'g') AS query
		FROM
				pg_stat_statements
		WHERE
		calls > 100
		ORDER BY
				total_time / calls DESC
		LIMIT 15
		) AS inner_table
		WHERE
		  per_call > 5
`

// https://gist.github.com/mattsoldo/3853455
var IndexCacheHitRateSQL = `
		-- Index hit rate
		WITH idx_hit_rate as (
		SELECT
		  relname as table_name,
		  n_live_tup,
		  round(100.0 * idx_scan / (seq_scan + idx_scan + 0.000001),2) as idx_hit_rate
		FROM pg_stat_user_tables
		ORDER BY n_live_tup DESC
		),

		-- Cache hit rate
		cache_hit_rate as (
		SELECT
		 relname as table_name,
		 heap_blks_read + heap_blks_hit as reads,
		 round(100.0 * sum (heap_blks_read + heap_blks_hit) over (ORDER BY heap_blks_read + heap_blks_hit DESC) / sum(heap_blks_read + heap_blks_hit + 0.000001) over (),4) as cumulative_pct_reads,
		 round(100.0 * heap_blks_hit / (heap_blks_hit + heap_blks_read + 0.000001),2) as cache_hit_rate
		FROM  pg_statio_user_tables
		WHERE heap_blks_hit + heap_blks_read > 0
		ORDER BY 2 DESC
		)

		SELECT
		  idx_hit_rate.table_name,
		  idx_hit_rate.n_live_tup as size,
		  cache_hit_rate.reads,
		  cache_hit_rate.cumulative_pct_reads,
		  idx_hit_rate.idx_hit_rate,
		  cache_hit_rate.cache_hit_rate
		FROM idx_hit_rate, cache_hit_rate
		WHERE idx_hit_rate.table_name = cache_hit_rate.table_name
		  AND cumulative_pct_reads < 100.0
		ORDER BY reads DESC;
`

// TableSizeSQL - XXX
var TableSizeSQL = `
		SELECT
			C .relname AS NAME,
			CASE
		WHEN C .relkind = 'r' THEN
			'table'
		ELSE
			'index'
		END AS TYPE,
		 pg_table_size(C .oid) AS SIZE
		FROM
			pg_class C
		LEFT JOIN pg_namespace n ON (n.oid = C .relnamespace)
		WHERE
			n.nspname NOT IN (
				'pg_catalog',
				'information_schema'
			)
		AND n.nspname !~ '^pg_toast'
		AND C .relkind IN ('r', 'i')
		ORDER BY
			pg_table_size (C .oid) DESC,
			NAME ASC
`

// IndexHitRateData - XXX
type IndexHitRateData struct {
	Headers []string      `json:"headers"`
	Data    []interface{} `json:"data"`
}

// TableSizeData - XXX
type TableSizeData struct {
	Headers []string      `json:"headers"`
	Data    []interface{} `json:"data"`
}

// SlowQueriesData - XXX
type SlowQueriesData struct {
	Headers []string      `json:"headers"`
	Data    []interface{} `json:"data"`
}

func (p PerformanceStruct) String() string {
	s, _ := json.Marshal(p)
	return string(s)
}

// Config - XXX
type Config struct {
	Host string
	DB   string
}

var sampleConfig = `
#   Available config options:
#
#    {"host": "postgres://user:password@localhost:port/dbname"}
#
# Config location: /etc/opt/amonagent/plugins-enabled/postgresql.conf
`

// SampleConfig - XXX
func (p *PostgreSQL) SampleConfig() string {
	return sampleConfig
}

// SetConfigDefaults - XXX
func (p *PostgreSQL) SetConfigDefaults(configPath string) error {
	c, err := plugins.ReadConfigPath(configPath)
	if err != nil {
		fmt.Printf("Can't read config file: %s %v\n", configPath, err)
	}
	var config Config
	decodeError := mapstructure.Decode(c, &config)
	if decodeError != nil {
		fmt.Print("Can't decode config file", decodeError.Error())
	}

	u, _ := url.Parse(config.Host)
	config.DB = strings.Trim(u.Path, "/")

	p.Config = config

	return nil
}

// PostgreSQL - XXX
type PostgreSQL struct {
	Config Config
}

// PerformanceStruct - XXX
type PerformanceStruct struct {
	TableSizeData    `json:"tables_size"`
	IndexHitRateData `json:"index_hit_rate"`
	SlowQueriesData  `json:"slow_queries"`
	Gauges           map[string]interface{} `json:"gauges"`
	Counters         map[string]interface{} `json:"counters"`
}

// Description - XXX
func (p *PostgreSQL) Description() string {
	return "Read metrics from a PostgreSQL server"
}

// Collect - XXX
func (p *PostgreSQL) Collect(configPath string) (interface{}, error) {
	PerformanceStruct := PerformanceStruct{}
	p.SetConfigDefaults(configPath)

	db, err := sql.Open("postgres", p.Config.Host)
	if err != nil {
		pluginLogger.Errorf("Can't connect to database': %v", err)
		return PerformanceStruct, err
	}

	defer db.Close()

	rawResult := make([][]byte, len(Counters))
	counters := make(map[string]interface{})
	dest := make([]interface{}, len(Counters)) // A temporary interface{} slice
	for i := range rawResult {
		dest[i] = &rawResult[i] // Put pointers to each string in the interface slice
	}
	var counterColumns []string
	for key := range Counters {
		counterColumns = append(counterColumns, key)
	}
	CountersQuery := fmt.Sprintf(DatabaseStatsSQL, strings.Join(counterColumns, ", "), p.Config.DB)
	CounterRows, errCounterRows := db.Query(CountersQuery)
	if errCounterRows == nil {

		for CounterRows.Next() {
			err = CounterRows.Scan(dest...)
			if err != nil {
				pluginLogger.Errorf("Can't get Counter stats': %v", err)

			}

			for i, val := range rawResult {
				key := counterColumns[i]
				counters[key] = string(val)
			}
		}
	}
	defer CounterRows.Close()

	gauges := make(map[string]interface{})
	GaugesQuery := fmt.Sprintf(DatabaseStatsSQL, "numbackends", "amon")
	GaugeRows, errGaugeRows := db.Query(GaugesQuery)
	defer GaugeRows.Close()
	if errGaugeRows == nil {

		for GaugeRows.Next() {
			var connections int
			err = GaugeRows.Scan(&connections)
			if err != nil {
				pluginLogger.Errorf("Can't get Gauges': %v", err)

			}
			gauges["connections"] = connections

		}

	}

	TableSizeRows, errTableSizeRows := db.Query(TableSizeSQL)
	TableSizeHeaders := []string{"name", "type", "size"}
	TableSizeData := TableSizeData{Headers: TableSizeHeaders}

	defer TableSizeRows.Close()
	if errTableSizeRows == nil {

		for TableSizeRows.Next() {
			// TABLES_SIZE_ROWS = ['name','type','size']
			var name string
			var Type string
			var size int64

			err = TableSizeRows.Scan(&name, &Type, &size)
			if err != nil {
				pluginLogger.Errorf("Can't get Table size rows': %v", err)
			}
			fields := []interface{}{}
			fields = append(fields, name)
			fields = append(fields, Type)
			fields = append(fields, size)

			TableSizeData.Data = append(TableSizeData.Data, fields)

		}

	}

	PerformanceStruct.TableSizeData = TableSizeData

	IndexHitRows, errIndexHitRows := db.Query(IndexCacheHitRateSQL)
	IndexHitHeaders := []string{"table_name", "size", "reads",
		"cumulative_pct_reads", "index_hit_rate", "cache_hit_rate"}
	IndexHitData := IndexHitRateData{Headers: IndexHitHeaders}
	defer IndexHitRows.Close()

	if errIndexHitRows == nil {

		for IndexHitRows.Next() {
			// INDEX_CACHE_HIT_RATE_ROWS = ['table_name','size','reads',
			// 'cumulative_pct_reads', 'index_hit_rate', 'cache_hit_rate']

			var TableName string
			var Size string
			var Read int64
			var CumulativeReads float64
			var IndexHitRate float64
			var CacheHitRate float64

			err = IndexHitRows.Scan(&TableName, &Size, &Read, &CumulativeReads, &IndexHitRate, &CacheHitRate)
			if err != nil {
				pluginLogger.Errorf("Can't get Index Hit Rate tables': %v", err)

			}
			fields := []interface{}{}
			fields = append(fields, TableName)
			fields = append(fields, Size)
			fields = append(fields, Read)
			fields = append(fields, CumulativeReads)
			fields = append(fields, IndexHitRate)
			fields = append(fields, CacheHitRate)

			IndexHitData.Data = append(IndexHitData.Data, fields)

		}
	}

	PerformanceStruct.IndexHitRateData = IndexHitData

	SlowQueriesRows, errSlowQueriesRows := db.Query(SlowQueriesSQL)
	SlowQueriesHeaders := []string{"calls", "total", "per_call", "query"}
	SlowQueriesData := SlowQueriesData{Headers: SlowQueriesHeaders}

	defer SlowQueriesRows.Close()

	if errSlowQueriesRows == nil {
		for SlowQueriesRows.Next() {

			var Calls int64
			var Total float64
			var PerCall float64
			var Query string

			err = SlowQueriesRows.Scan(&Calls, &Total, &PerCall, &Query)
			if err != nil {
				pluginLogger.Errorf("Can't get Slow Queries': %v", err)
			}
			fields := []interface{}{}
			fields = append(fields, Calls)
			fields = append(fields, Total)
			fields = append(fields, PerCall)
			fields = append(fields, Query)

			SlowQueriesData.Data = append(SlowQueriesData.Data, fields)

		}
	}

	PerformanceStruct.SlowQueriesData = SlowQueriesData

	PerformanceStruct.Counters = counters
	PerformanceStruct.Gauges = gauges

	return PerformanceStruct, nil
}

func init() {
	plugins.Add("postgresql", func() plugins.Plugin {
		return &PostgreSQL{}
	})
}
<commit_msg>Update postgres plugin<commit_after>package postgresql

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"net/url"
	"strings"

	"github.com/amonapp/amonagent/logging"
	"github.com/amonapp/amonagent/plugins"
	"github.com/mitchellh/mapstructure"
	// Postgres Driver
	_ "github.com/lib/pq"
)

var pluginLogger = logging.GetLogger("amonagent.postgresql")

// Counters - XXX
var Counters = map[string]string{
	"xact_commit":   "xact.commits",
	"xact_rollback": "xact.rollbacks",
	"blks_read":     "performance.disk_read",
	"blks_hit":      "performance.buffer_hit",
	"tup_returned":  "rows.returned",
	"tup_fetched":   "rows.fetched",
	"tup_inserted":  "rows.inserted",
	"tup_updated":   "rows.updated",
	"tup_deleted":   "rows.deleted",
}

// Gauges - XXX
var Gauges = map[string]string{
	"numbackends": "connections",
}

// DatabaseStatsSQL - XXX
var DatabaseStatsSQL = `SELECT %s FROM pg_stat_database WHERE datname IN ('%s')`

// MissingIndexesSQL - XXX
var MissingIndexesSQL = `SELECT
		  relname AS table,
		  CASE idx_scan
			WHEN 0 THEN 'Insufficient data'
			ELSE (100 * idx_scan / (seq_scan + idx_scan))::text
		  END percent_of_times_index_used,
		  n_live_tup rows_in_table
		FROM
		  pg_stat_user_tables
		WHERE
		  idx_scan > 0
		  AND (100 * idx_scan / (seq_scan + idx_scan)) < 95
		  AND n_live_tup >= 10000
		ORDER BY
		  n_live_tup DESC,
		  relname ASC
`

// SlowQueriesSQL - XXX
var SlowQueriesSQL = `SELECT * FROM
		(SELECT
				calls,
				round(total_time :: NUMERIC, 1) AS total,
				round(
					(total_time / calls) :: NUMERIC,
					3
				) AS per_call,
				regexp_replace(query, '[ \t\n]+', ' ', 'g') AS query
		FROM
				pg_stat_statements
		WHERE
		calls > 100
		ORDER BY
				total_time / calls DESC
		LIMIT 15
		) AS inner_table
		WHERE
		  per_call > 5
`

// https://gist.github.com/mattsoldo/3853455
var IndexCacheHitRateSQL = `
		-- Index hit rate
		WITH idx_hit_rate as (
		SELECT
		  relname as table_name,
		  n_live_tup,
		  round(100.0 * idx_scan / (seq_scan + idx_scan + 0.000001),2) as idx_hit_rate
		FROM pg_stat_user_tables
		ORDER BY n_live_tup DESC
		),

		-- Cache hit rate
		cache_hit_rate as (
		SELECT
		 relname as table_name,
		 heap_blks_read + heap_blks_hit as reads,
		 round(100.0 * sum (heap_blks_read + heap_blks_hit) over (ORDER BY heap_blks_read + heap_blks_hit DESC) / sum(heap_blks_read + heap_blks_hit + 0.000001) over (),4) as cumulative_pct_reads,
		 round(100.0 * heap_blks_hit / (heap_blks_hit + heap_blks_read + 0.000001),2) as cache_hit_rate
		FROM  pg_statio_user_tables
		WHERE heap_blks_hit + heap_blks_read > 0
		ORDER BY 2 DESC
		)

		SELECT
		  idx_hit_rate.table_name,
		  idx_hit_rate.n_live_tup as size,
		  cache_hit_rate.reads,
		  cache_hit_rate.cumulative_pct_reads,
		  idx_hit_rate.idx_hit_rate,
		  cache_hit_rate.cache_hit_rate
		FROM idx_hit_rate, cache_hit_rate
		WHERE idx_hit_rate.table_name = cache_hit_rate.table_name
		  AND cumulative_pct_reads < 100.0
		ORDER BY reads DESC;
`

// TableSizeSQL - XXX
var TableSizeSQL = `
		SELECT
			C .relname AS NAME,
			CASE
		WHEN C .relkind = 'r' THEN
			'table'
		ELSE
			'index'
		END AS TYPE,
		 pg_table_size(C .oid) AS SIZE
		FROM
			pg_class C
		LEFT JOIN pg_namespace n ON (n.oid = C .relnamespace)
		WHERE
			n.nspname NOT IN (
				'pg_catalog',
				'information_schema'
			)
		AND n.nspname !~ '^pg_toast'
		AND C .relkind IN ('r', 'i')
		ORDER BY
			pg_table_size (C .oid) DESC,
			NAME ASC
`

// IndexHitRateData - XXX
type IndexHitRateData struct {
	Headers []string      `json:"headers"`
	Data    []interface{} `json:"data"`
}

// TableSizeData - XXX
type TableSizeData struct {
	Headers []string      `json:"headers"`
	Data    []interface{} `json:"data"`
}

// SlowQueriesData - XXX
type SlowQueriesData struct {
	Headers []string      `json:"headers"`
	Data    []interface{} `json:"data"`
}

func (p PerformanceStruct) String() string {
	s, _ := json.Marshal(p)
	return string(s)
}

// Config - XXX
type Config struct {
	Host string
	DB   string
}

var sampleConfig = `
#   Available config options:
#
#    {"host": "postgres://user:password@localhost:port/dbname"}
#
# Config location: /etc/opt/amonagent/plugins-enabled/postgresql.conf
`

// SampleConfig - XXX
func (p *PostgreSQL) SampleConfig() string {
	return sampleConfig
}

// SetConfigDefaults - XXX
func (p *PostgreSQL) SetConfigDefaults(configPath string) error {
	c, err := plugins.ReadConfigPath(configPath)
	if err != nil {
		fmt.Printf("Can't read config file: %s %v\n", configPath, err)
	}
	var config Config
	decodeError := mapstructure.Decode(c, &config)
	if decodeError != nil {
		fmt.Print("Can't decode config file", decodeError.Error())
	}

	u, _ := url.Parse(config.Host)
	config.DB = strings.Trim(u.Path, "/")

	p.Config = config

	return nil
}

// PostgreSQL - XXX
type PostgreSQL struct {
	Config Config
}

// PerformanceStruct - XXX
type PerformanceStruct struct {
	TableSizeData    `json:"tables_size"`
	IndexHitRateData `json:"index_hit_rate"`
	SlowQueriesData  `json:"slow_queries"`
	Gauges           map[string]interface{} `json:"gauges"`
	Counters         map[string]interface{} `json:"counters"`
}

// Description - XXX
func (p *PostgreSQL) Description() string {
	return "Read metrics from a PostgreSQL server"
}

// Collect - XXX
func (p *PostgreSQL) Collect(configPath string) (interface{}, error) {
	PerformanceStruct := PerformanceStruct{}
	p.SetConfigDefaults(configPath)

	db, err := sql.Open("postgres", p.Config.Host)
	if err != nil {
		pluginLogger.Errorf("Can't connect to database': %v", err)
		return PerformanceStruct, err
	}

	defer db.Close()

	rawResult := make([][]byte, len(Counters))
	counters := make(map[string]interface{})
	dest := make([]interface{}, len(Counters)) // A temporary interface{} slice
	for i := range rawResult {
		dest[i] = &rawResult[i] // Put pointers to each string in the interface slice
	}
	var counterColumns []string
	for key := range Counters {
		counterColumns = append(counterColumns, key)
	}
	CountersQuery := fmt.Sprintf(DatabaseStatsSQL, strings.Join(counterColumns, ", "), p.Config.DB)
	CounterRows, errCounterRows := db.Query(CountersQuery)
	if errCounterRows == nil {
		defer CounterRows.Close()

		for CounterRows.Next() {
			err = CounterRows.Scan(dest...)
			if err != nil {
				pluginLogger.Errorf("Can't get Counter stats': %v", err)

			}

			for i, val := range rawResult {
				key := counterColumns[i]
				counters[key] = string(val)
			}
		}
	}

	gauges := make(map[string]interface{})
	GaugesQuery := fmt.Sprintf(DatabaseStatsSQL, "numbackends", "amon")
	GaugeRows, errGaugeRows := db.Query(GaugesQuery)

	if errGaugeRows == nil {
		defer GaugeRows.Close()

		for GaugeRows.Next() {
			var connections int
			err = GaugeRows.Scan(&connections)
			if err != nil {
				pluginLogger.Errorf("Can't get Gauges': %v", err)

			}
			gauges["connections"] = connections

		}

	}

	TableSizeRows, errTableSizeRows := db.Query(TableSizeSQL)
	TableSizeHeaders := []string{"name", "type", "size"}
	TableSizeData := TableSizeData{Headers: TableSizeHeaders}

	if errTableSizeRows == nil {
		defer TableSizeRows.Close()

		for TableSizeRows.Next() {
			// TABLES_SIZE_ROWS = ['name','type','size']
			var name string
			var Type string
			var size int64

			err = TableSizeRows.Scan(&name, &Type, &size)
			if err != nil {
				pluginLogger.Errorf("Can't get Table size rows': %v", err)
			}
			fields := []interface{}{}
			fields = append(fields, name)
			fields = append(fields, Type)
			fields = append(fields, size)

			TableSizeData.Data = append(TableSizeData.Data, fields)

		}

		PerformanceStruct.TableSizeData = TableSizeData

	}

	IndexHitRows, errIndexHitRows := db.Query(IndexCacheHitRateSQL)
	IndexHitHeaders := []string{"table_name", "size", "reads",
		"cumulative_pct_reads", "index_hit_rate", "cache_hit_rate"}
	IndexHitData := IndexHitRateData{Headers: IndexHitHeaders}

	if errIndexHitRows == nil {
		defer IndexHitRows.Close()
		for IndexHitRows.Next() {
			// INDEX_CACHE_HIT_RATE_ROWS = ['table_name','size','reads',
			// 'cumulative_pct_reads', 'index_hit_rate', 'cache_hit_rate']

			var TableName string
			var Size string
			var Read int64
			var CumulativeReads float64
			var IndexHitRate float64
			var CacheHitRate float64

			err = IndexHitRows.Scan(&TableName, &Size, &Read, &CumulativeReads, &IndexHitRate, &CacheHitRate)
			if err != nil {
				pluginLogger.Errorf("Can't get Index Hit Rate tables': %v", err)

			}
			fields := []interface{}{}
			fields = append(fields, TableName)
			fields = append(fields, Size)
			fields = append(fields, Read)
			fields = append(fields, CumulativeReads)
			fields = append(fields, IndexHitRate)
			fields = append(fields, CacheHitRate)

			IndexHitData.Data = append(IndexHitData.Data, fields)

		}

		PerformanceStruct.IndexHitRateData = IndexHitData
	}

	SlowQueriesRows, errSlowQueriesRows := db.Query(SlowQueriesSQL)
	SlowQueriesHeaders := []string{"calls", "total", "per_call", "query"}
	SlowQueriesData := SlowQueriesData{Headers: SlowQueriesHeaders}

	if errSlowQueriesRows == nil {
		defer SlowQueriesRows.Close()
		for SlowQueriesRows.Next() {

			var Calls int64
			var Total float64
			var PerCall float64
			var Query string

			err = SlowQueriesRows.Scan(&Calls, &Total, &PerCall, &Query)
			if err != nil {
				pluginLogger.Errorf("Can't get Slow Queries': %v", err)
			}
			fields := []interface{}{}
			fields = append(fields, Calls)
			fields = append(fields, Total)
			fields = append(fields, PerCall)
			fields = append(fields, Query)

			SlowQueriesData.Data = append(SlowQueriesData.Data, fields)

		}

		PerformanceStruct.SlowQueriesData = SlowQueriesData
	}

	PerformanceStruct.Counters = counters
	PerformanceStruct.Gauges = gauges

	return PerformanceStruct, nil
}

func init() {
	plugins.Add("postgresql", func() plugins.Plugin {
		return &PostgreSQL{}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"strings"
	"time"
)

// Match represents a game being played
type Match struct {
	Players    []Player  `json:"players"`
	Judges     []Judge   `json:"judges"`
	Kind       string    `json:"kind"`
	Index      int       `json:"index"`
	Started    time.Time `json:"started"`
	Ended      time.Time `json:"ended"`
	tournament *Tournament
}

// NewMatch creates a new Match for usage!
func NewMatch(t *Tournament, index int, kind string) *Match {
	return &Match{
		Index:      index,
		Kind:       kind,
		tournament: t,
	}
}

func (m *Match) String() string {
	var tempo string
	var name string

	if !m.IsStarted() {
		tempo = "not started"
	} else if m.IsEnded() {
		tempo = "ended"
	} else {
		tempo = "playing"
	}

	if m.Kind == "final" {
		name = "Final"
	} else {
		name = fmt.Sprintf("%s %d", strings.Title(m.Kind), m.Index+1)
	}

	names := make([]string, 0, len(m.Players))
	for _, p := range m.Players {
		names = append(names, p.Name)
	}

	return fmt.Sprintf(
		"<%s: %s - %s>",
		name,
		strings.Join(names, " / "),
		tempo,
	)
}

// AddPlayer adds a player to the match
func (m *Match) AddPlayer(p Player) error {
	if len(m.Players) == 4 {
		return errors.New("cannot add fifth player")
	}

	m.Players = append(m.Players, p)
	return nil
}

// Prefill fills remaining player slots with nil players
func (m *Match) Prefill() error {
	for i := len(m.Players); i < 4; i++ {
		m.AddPlayer(Player{})
	}
	return nil
}

// Start starts the match
func (m *Match) Start() error {
	if !m.Started.IsZero() {
		return errors.New("match already started")
	}

	// If there are not four players in the match, we need to populate
	// the match with runnerups from the tournament
	if len(m.Players) != 4 {
		m.tournament.PopulateRunnerups(m)
	}

	for i := range m.Players {
		m.Players[i].Reset()
		m.Players[i].match = m
	}

	m.Started = time.Now()
	return nil
}

// End signals that the match has ended
//
// It is also the place that moves players into either the Runnerup bracket
// or into their place in the semis.
func (m *Match) End() error {
	if !m.Ended.IsZero() {
		return errors.New("match already ended")
	}

	if m.Kind == "final" {
		m.tournament.AwardMedals(m)
	} else {
		m.tournament.MovePlayers(m)
	}
	m.Ended = time.Now()
	return nil
}

// IsStarted returns boolean whether the match has started or not
func (m *Match) IsStarted() bool {
	return !m.Started.IsZero()
}

// IsEnded returns boolean whether the match has ended or not
func (m *Match) IsEnded() bool {
	return !m.Ended.IsZero()
}
<commit_msg>Add prefilling on Match creation<commit_after>package main

import (
	"errors"
	"fmt"
	"strings"
	"time"
)

// Match represents a game being played
type Match struct {
	Players    []Player  `json:"players"`
	Judges     []Judge   `json:"judges"`
	Kind       string    `json:"kind"`
	Index      int       `json:"index"`
	Started    time.Time `json:"started"`
	Ended      time.Time `json:"ended"`
	tournament *Tournament
}

// NewMatch creates a new Match for usage!
func NewMatch(t *Tournament, index int, kind string) *Match {
	m := Match{
		Index:      index,
		Kind:       kind,
		tournament: t,
	}
	m.Prefill()
	return &m
}

func (m *Match) String() string {
	var tempo string
	var name string

	if !m.IsStarted() {
		tempo = "not started"
	} else if m.IsEnded() {
		tempo = "ended"
	} else {
		tempo = "playing"
	}

	if m.Kind == "final" {
		name = "Final"
	} else {
		name = fmt.Sprintf("%s %d", strings.Title(m.Kind), m.Index+1)
	}

	names := make([]string, 0, len(m.Players))
	for _, p := range m.Players {
		names = append(names, p.Name)
	}

	return fmt.Sprintf(
		"<%s: %s - %s>",
		name,
		strings.Join(names, " / "),
		tempo,
	)
}

// AddPlayer adds a player to the match
func (m *Match) AddPlayer(p Player) error {
	if len(m.Players) == 4 {
		return errors.New("cannot add fifth player")
	}

	m.Players = append(m.Players, p)
	return nil
}

// Prefill fills remaining player slots with nil players
func (m *Match) Prefill() error {
	for i := len(m.Players); i < 4; i++ {
		m.AddPlayer(Player{})
	}
	return nil
}

// Start starts the match
func (m *Match) Start() error {
	if !m.Started.IsZero() {
		return errors.New("match already started")
	}

	// If there are not four players in the match, we need to populate
	// the match with runnerups from the tournament
	if len(m.Players) != 4 {
		m.tournament.PopulateRunnerups(m)
	}

	for i := range m.Players {
		m.Players[i].Reset()
		m.Players[i].match = m
	}

	m.Started = time.Now()
	return nil
}

// End signals that the match has ended
//
// It is also the place that moves players into either the Runnerup bracket
// or into their place in the semis.
func (m *Match) End() error {
	if !m.Ended.IsZero() {
		return errors.New("match already ended")
	}

	if m.Kind == "final" {
		m.tournament.AwardMedals(m)
	} else {
		m.tournament.MovePlayers(m)
	}
	m.Ended = time.Now()
	return nil
}

// IsStarted returns boolean whether the match has started or not
func (m *Match) IsStarted() bool {
	return !m.Started.IsZero()
}

// IsEnded returns boolean whether the match has ended or not
func (m *Match) IsEnded() bool {
	return !m.Ended.IsZero()
}
<|endoftext|>