<commit_before>package main

import (
	"bufio"
	"crypto/md5"
	"flag"
	"fmt"
	"io"
	"net"
	"strings"

	mp "github.com/mackerelio/go-mackerel-plugin-helper"
)

// MemcachedPlugin mackerel plugin for memchached
type MemcachedPlugin struct {
	Target   string
	Socket   string
	Tempfile string
	Prefix   string
}

// FetchMetrics interface for mackerelplugin
func (m MemcachedPlugin) FetchMetrics() (map[string]interface{}, error) {
	network := "tcp"
	target := m.Target
	if m.Socket != "" {
		network = "unix"
		target = m.Socket
	}
	conn, err := net.Dial(network, target)
	if err != nil {
		return nil, err
	}
	fmt.Fprintln(conn, "stats")
	return m.parseStats(conn)
}

func (m MemcachedPlugin) parseStats(conn io.Reader) (map[string]interface{}, error) {
	scanner := bufio.NewScanner(conn)
	stat := make(map[string]interface{})

	for scanner.Scan() {
		line := scanner.Text()
		s := string(line)
		if s == "END" {
			return stat, nil
		}

		res := strings.Split(s, " ")
		if res[0] == "STAT" {
			stat[res[1]] = res[2]
		}
	}
	if err := scanner.Err(); err != nil {
		return stat, err
	}
	return nil, nil
}

// GraphDefinition interface for mackerelplugin
func (m MemcachedPlugin) GraphDefinition() map[string](mp.Graphs) {
	// https://github.com/memcached/memcached/blob/master/doc/protocol.txt
	var graphdef = map[string](mp.Graphs){
		(m.Prefix + ".connections"): mp.Graphs{
			Label: "Memcached Connections",
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "curr_connections", Label: "Connections", Diff: false},
			},
		},
		(m.Prefix + ".cmd"): mp.Graphs{
			Label: "Memcached Command",
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "cmd_get", Label: "Get", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "cmd_set", Label: "Set", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "cmd_flush", Label: "Flush", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "cmd_touch", Label: "Touch", Diff: true, Type: "uint64"},
			},
		},
		(m.Prefix + ".hitmiss"): mp.Graphs{
			Label: "Memcached Hits/Misses",
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "get_hits", Label: "Get Hits", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "get_misses", Label: "Get Misses", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "delete_hits", Label: "Delete Hits", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "delete_misses", Label: "Delete Misses", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "incr_hits", Label: "Incr Hits", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "incr_misses", Label: "Incr Misses", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "cas_hits", Label: "Cas Hits", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "cas_misses", Label: "Cas Misses", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "touch_hits", Label: "Touch Hits", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "touch_misses", Label: "Touch Misses", Diff: true, Type: "uint64"},
			},
		},
		(m.Prefix + ".evictions"): mp.Graphs{
			Label: "Memcached Evictions",
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "evictions", Label: "Evictions", Diff: true, Type: "uint64"},
			},
		},
		(m.Prefix + ".unfetched"): mp.Graphs{
			Label: "Memcached Unfetched",
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "expired_unfetched", Label: "Expired unfetched", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "evicted_unfetched", Label: "Evicted unfetched", Diff: true, Type: "uint64"},
			},
		},
		(m.Prefix + ".rusage"): mp.Graphs{
			Label: "Memcached Resouce Usage",
			Unit:  "float",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "rusage_user", Label: "User", Diff: true},
				mp.Metrics{Name: "rusage_system", Label: "System", Diff: true},
			},
		},
		(m.Prefix + ".bytes"): mp.Graphs{
			Label: "Memcached Traffics",
			Unit:  "bytes",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "bytes_read", Label: "Read", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "bytes_written", Label: "Write", Diff: true, Type: "uint64"},
			},
		},
		(m.Prefix + ".cachesize"): mp.Graphs{
			Label: "Memcached Cache Size",
			Unit:  "bytes",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "limit_maxbytes", Label: "Total", Diff: false},
				mp.Metrics{Name: "bytes", Label: "Used", Diff: false, Type: "uint64"},
			},
		},
	}
	return graphdef
}

func main() {
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "11211", "Port")
	optSocket := flag.String("socket", "", "Server socket (overrides hosts and port)")
	optPrefix := flag.String("metric-key-prefix", "memcached", "Metric key prefix")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var memcached MemcachedPlugin

	memcached.Prefix = *optPrefix

	if *optSocket != "" {
		memcached.Socket = *optSocket
	} else {
		memcached.Target = fmt.Sprintf("%s:%s", *optHost, *optPort)
	}
	helper := mp.NewMackerelPlugin(memcached)

	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		if memcached.Socket != "" {
			helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-memcached-%s", fmt.Sprintf("%x", md5.Sum([]byte(memcached.Socket))))
		} else {
			helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-memcached-%s-%s", *optHost, *optPort)
		}
	}
	helper.Run()
}
<commit_msg>change Label prefix if metric-key-prefix option is specified<commit_after>package main

import (
	"bufio"
	"crypto/md5"
	"flag"
	"fmt"
	"io"
	"net"
	"strings"

	mp "github.com/mackerelio/go-mackerel-plugin-helper"
)

// MemcachedPlugin mackerel plugin for memchached
type MemcachedPlugin struct {
	Target   string
	Socket   string
	Tempfile string
	Prefix   string
}

// FetchMetrics interface for mackerelplugin
func (m MemcachedPlugin) FetchMetrics() (map[string]interface{}, error) {
	network := "tcp"
	target := m.Target
	if m.Socket != "" {
		network = "unix"
		target = m.Socket
	}
	conn, err := net.Dial(network, target)
	if err != nil {
		return nil, err
	}
	fmt.Fprintln(conn, "stats")
	return m.parseStats(conn)
}

func (m MemcachedPlugin) parseStats(conn io.Reader) (map[string]interface{}, error) {
	scanner := bufio.NewScanner(conn)
	stat := make(map[string]interface{})

	for scanner.Scan() {
		line := scanner.Text()
		s := string(line)
		if s == "END" {
			return stat, nil
		}

		res := strings.Split(s, " ")
		if res[0] == "STAT" {
			stat[res[1]] = res[2]
		}
	}
	if err := scanner.Err(); err != nil {
		return stat, err
	}
	return nil, nil
}

// GraphDefinition interface for mackerelplugin
func (m MemcachedPlugin) GraphDefinition() map[string](mp.Graphs) {
	labelPrefix := strings.Title(m.Prefix)

	// https://github.com/memcached/memcached/blob/master/doc/protocol.txt
	var graphdef = map[string](mp.Graphs){
		(m.Prefix + ".connections"): mp.Graphs{
			Label: (labelPrefix + " Connections"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "curr_connections", Label: "Connections", Diff: false},
			},
		},
		(m.Prefix + ".cmd"): mp.Graphs{
			Label: (labelPrefix + " Command"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "cmd_get", Label: "Get", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "cmd_set", Label: "Set", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "cmd_flush", Label: "Flush", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "cmd_touch", Label: "Touch", Diff: true, Type: "uint64"},
			},
		},
		(m.Prefix + ".hitmiss"): mp.Graphs{
			Label: (labelPrefix + " Hits/Misses"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "get_hits", Label: "Get Hits", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "get_misses", Label: "Get Misses", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "delete_hits", Label: "Delete Hits", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "delete_misses", Label: "Delete Misses", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "incr_hits", Label: "Incr Hits", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "incr_misses", Label: "Incr Misses", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "cas_hits", Label: "Cas Hits", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "cas_misses", Label: "Cas Misses", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "touch_hits", Label: "Touch Hits", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "touch_misses", Label: "Touch Misses", Diff: true, Type: "uint64"},
			},
		},
		(m.Prefix + ".evictions"): mp.Graphs{
			Label: (labelPrefix + " Evictions"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "evictions", Label: "Evictions", Diff: true, Type: "uint64"},
			},
		},
		(m.Prefix + ".unfetched"): mp.Graphs{
			Label: (labelPrefix + " Unfetched"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "expired_unfetched", Label: "Expired unfetched", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "evicted_unfetched", Label: "Evicted unfetched", Diff: true, Type: "uint64"},
			},
		},
		(m.Prefix + ".rusage"): mp.Graphs{
			Label: (labelPrefix + " Resouce Usage"),
			Unit:  "float",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "rusage_user", Label: "User", Diff: true},
				mp.Metrics{Name: "rusage_system", Label: "System", Diff: true},
			},
		},
		(m.Prefix + ".bytes"): mp.Graphs{
			Label: (labelPrefix + " Traffics"),
			Unit:  "bytes",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "bytes_read", Label: "Read", Diff: true, Type: "uint64"},
				mp.Metrics{Name: "bytes_written", Label: "Write", Diff: true, Type: "uint64"},
			},
		},
		(m.Prefix + ".cachesize"): mp.Graphs{
			Label: (labelPrefix + " Cache Size"),
			Unit:  "bytes",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "limit_maxbytes", Label: "Total", Diff: false},
				mp.Metrics{Name: "bytes", Label: "Used", Diff: false, Type: "uint64"},
			},
		},
	}
	return graphdef
}

func main() {
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "11211", "Port")
	optSocket := flag.String("socket", "", "Server socket (overrides hosts and port)")
	optPrefix := flag.String("metric-key-prefix", "memcached", "Metric key prefix")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var memcached MemcachedPlugin

	memcached.Prefix = *optPrefix

	if *optSocket != "" {
		memcached.Socket = *optSocket
	} else {
		memcached.Target = fmt.Sprintf("%s:%s", *optHost, *optPort)
	}
	helper := mp.NewMackerelPlugin(memcached)

	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		if memcached.Socket != "" {
			helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-memcached-%s", fmt.Sprintf("%x", md5.Sum([]byte(memcached.Socket))))
		} else {
			helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-memcached-%s-%s", *optHost, *optPort)
		}
	}
	helper.Run()
}
<|endoftext|>


<commit_before>package binary

import (
	"bytes"
	"bufio"
	"io"

	"golang.org/x/text/encoding/japanese"
	"golang.org/x/text/transform"
)

//Conversion
func Conversion(inStream io.Reader, outStream io.Writer) error {
	//read from stream (Shift-JIS to UTF-8)
	buf := new(bytes.Buffer)
	if _, err := buf.ReadFrom(transform.NewReader(inStream, japanese.ShiftJIS.NewDecoder())); err != nil {
		return err
	}

	//write to stream (UTF-8 to EUC-JP)
	writer := bufio.NewWriter(transform.NewWriter(outStream, japanese.EUCJP.NewEncoder()))
	if _, err := writer.Write(buf.Bytes()); err != nil {
		return err
	}
	return writer.Flush()
}
<commit_msg>update conversion.go<commit_after>package binary

import (
	"io"

	"golang.org/x/text/encoding/japanese"
	"golang.org/x/text/transform"
)

//Conversion
func Conversion(inStream io.Reader, outStream io.Writer) error {
	//reader from stream (Shift-JIS to UTF-8)
	reader := transform.NewReader(inStream, japanese.ShiftJIS.NewDecoder())

	//writer to stream (UTF-8 to EUC-JP)
	writer := transform.NewWriter(outStream, japanese.EUCJP.NewEncoder())

	//Copy
	_, err := io.Copy(writer, reader)
	return err
}
<|endoftext|>


<commit_before>package core

import (
	"os"
	"testing"
)

func TestMain(m *testing.M) { os.Exit(m.Run()) }

func BenchmarkPlus(b *testing.B) {

}
<commit_msg>benchmarks using the go testing framework make no sense<commit_after><|endoftext|>


<commit_before>// Package retrier implements the "retriable" resilliency pattern for Go.
package retrier

import "time"

// Retrier implements the "retriable" resiliency pattern, abstracting out the process of retrying a failed
// action a certain number of times with an optional back-off between each entry.
type Retrier struct {
	backoff    []time.Duration
	classifier Classifier
}

// New construct retrier with a certain number of backoffs
func New(backoff []time.Duration, classifier Classifier) *Retrier {
	return &Retrier{
		backoff:    backoff,
		classifier: classifier,
	}
}

// Run a
func (r *Retrier) Run(work func() error) error {
	retries := 0

	for {
		err := work()
		ret := r.classifier.Classify(err)

		switch ret {
		case Succeed:
			return nil
		case Fail:
			return err
		case Retry:
			if retries >= len(r.backoff) {
				return err
			}
			time.Sleep(r.backoff[retries])
			retries++
		}
	}
}
<commit_msg>refactor to simplify code<commit_after>// Package retrier implements the "retriable" resilliency pattern for Go.
package retrier

import "time"

// Retrier implements the "retriable" resiliency pattern, abstracting out the process of retrying a failed
// action a certain number of times with an optional back-off between each entry.
type Retrier struct {
	backoff    []time.Duration
	classifier Classifier
}

// New construct retrier with a certain number of backoffs
func New(backoff []time.Duration, classifier Classifier) *Retrier {
	return &Retrier{
		backoff:    backoff,
		classifier: classifier,
	}
}

// Run a
func (r *Retrier) Run(work func() error) error {
	retries := 0

	for {
		ret := work()
		switch r.classifier.Classify(ret) {
		case Succeed, Fail:
			return ret
		case Retry:
			if retries >= len(r.backoff) {
				return ret
			}
			time.Sleep(r.backoff[retries])
			retries++
		}
	}
}
<|endoftext|>


<commit_before>package phys

import (
	"github.com/ftrvxmtrx/gochipmunk/chipmunk"
	"github.com/ianremmler/gordian"

	"math"
	"math/rand"
	"time"
)

const (
	simTime    = time.Second / 72
	updateTime = time.Second / 24
	size       = 500.0
)

type ballInfo struct {
	Pos   chipmunk.Vect
	Angle float64
}

type clientData struct {
	body  chipmunk.Body
	joint chipmunk.Constraint
}

type Phys struct {
	clients     map[gordian.ClientId]clientData
	simTimer    <-chan time.Time
	updateTimer <-chan time.Time
	curId       int
	space       *chipmunk.Space
	ballBodies  []chipmunk.Body
	ballShapes  []chipmunk.Shape
	box         []chipmunk.Shape
	*gordian.Gordian
}

func NewPhys() *Phys {
	p := &Phys{
		clients:     map[gordian.ClientId]clientData{},
		simTimer:    time.Tick(simTime),
		updateTimer: time.Tick(updateTime),
		Gordian:     gordian.New(0),
	}
	p.setup()
	return p
}

func (p *Phys) setup() {
	p.ballBodies = make([]chipmunk.Body, 42)
	p.ballShapes = make([]chipmunk.Shape, len(p.ballBodies))
	p.box = make([]chipmunk.Shape, 4)
	p.space = chipmunk.SpaceNew()
	p.space.SetGravity(chipmunk.Vect{0, -100})

	radius, mass := 10.0, 1.0
	moment := chipmunk.MomentForCircle(mass, 0, radius, chipmunk.Origin())

	for i := range p.ballBodies {
		body := chipmunk.BodyNew(mass, moment)
		p.space.AddBody(body)
		p.ballBodies[i] = body
		shape := chipmunk.CircleShapeNew(body, radius, chipmunk.Origin())
		shape.SetFriction(0.5)
		shape.SetElasticity(0.9)
		shape.SetLayers(1)
		p.space.AddShape(shape)
		p.ballShapes[i] = shape
	}
	pts := []chipmunk.Vect{
		{-radius, -radius},
		{size + radius, -radius},
		{size + radius, size + radius},
		{-radius, size + radius},
	}
	for i := range p.box {
		shape := chipmunk.SegmentShapeNew(p.space.StaticBody(), pts[i],
			pts[(i+1)%len(pts)], radius)
		shape.SetElasticity(1.0)
		shape.SetFriction(1.0)
		p.space.AddShape(shape)
		p.box[i] = shape
	}
}

func (p *Phys) dropBalls() {
	for i := range p.ballBodies {
		radius := p.ballShapes[i].(chipmunk.CircleShape).Radius()
		p.ballBodies[i].SetPosition(chipmunk.Vect{0.5*size + (rand.Float64()*2-1)*radius,
			0.5*size + rand.Float64()*(0.5*size-2*radius)})
	}
}

func (p *Phys) Run() {
	go p.run()
	p.dropBalls()
	p.Gordian.Run()
}

func (p *Phys) run() {
	msg := gordian.Message{}
	rawData := map[string]interface{}{}
	balls := make([]ballInfo, len(p.ballBodies))
	for {
		select {
		case client := <-p.Control:
			switch client.Ctrl {
			case gordian.CONNECT:
				p.curId++
				client.Id = p.curId
				client.Ctrl = gordian.REGISTER
				body := chipmunk.BodyNew(math.Inf(0), math.Inf(0))
				p.clients[client.Id] = clientData{body: body}
				p.Control <- client
			case gordian.CLOSE:
				c, ok := p.clients[client.Id]
				if !ok {
					break
				}
				if c.joint != nil {
					p.space.RemoveConstraint(c.joint)
					c.joint.Free()
				}
				c.body.Free()
				delete(p.clients, client.Id)
			}
		case msg = <-p.InMessage:
			id := msg.From
			c, ok := p.clients[id]
			if !ok {
				break
			}
			rawData = msg.Data.(map[string]interface{})
			data := rawData["data"].(map[string]interface{})
			rawPos := data["pos"].(map[string]interface{})
			btn := data["btn"].(bool)
			pos := chipmunk.Vect{rawPos["x"].(float64), rawPos["y"].(float64)}
			c.body.SetPosition(pos)
			isDragging := (c.joint != nil)

			switch {
			case !isDragging && btn:
				shape := p.space.PointQueryFirst(pos, 1, chipmunk.NoGroup)
				if shape != nil {
					c.joint = chipmunk.PivotJointNew2(c.body, shape.Body(),
						chipmunk.Origin(), chipmunk.Origin())
					c.joint.SetMaxForce(10000.0)
					p.space.AddConstraint(c.joint)
				}
			case isDragging && !btn:
				if c.joint != nil {
					p.space.RemoveConstraint(c.joint)
					c.joint.Free()
					c.joint = nil
				}
			}
			p.clients[id] = c
		case <-p.simTimer:
			p.space.Step(float64(simTime) / float64(time.Second))
		case <-p.updateTimer:
			for i, bb := range p.ballBodies {
				pos := bb.Position()
				angle := bb.Angle()
				balls[i] = ballInfo{pos, angle}
			}
			data := map[string]interface{}{}
			data["balls"] = balls

			cursors := []chipmunk.Vect{}
			for _, client := range p.clients {
				cursors = append(cursors, client.body.Position())
			}

			data["cursors"] = cursors
			rawData["data"] = data
			msg.Data = rawData
			for id := range p.clients {
				msg.To = id
				p.OutMessage <- msg
			}
		}
	}
}
<commit_msg>Moved code to functions.<commit_after>package phys

import (
	"github.com/ftrvxmtrx/gochipmunk/chipmunk"
	"github.com/ianremmler/gordian"

	"math"
	"math/rand"
	"time"
)

const (
	simTime    = time.Second / 72
	updateTime = time.Second / 24
	size       = 500.0
	numBalls   = 42
)

type ballInfo struct {
	Pos   chipmunk.Vect
	Angle float64
}

type clientData struct {
	body  chipmunk.Body
	joint chipmunk.Constraint
}

type Phys struct {
	clients     map[gordian.ClientId]clientData
	simTimer    <-chan time.Time
	updateTimer <-chan time.Time
	curId       int
	space       *chipmunk.Space
	ballBodies  []chipmunk.Body
	ballShapes  []chipmunk.Shape
	ballList    []ballInfo
	box         []chipmunk.Shape
	*gordian.Gordian
}

func NewPhys() *Phys {
	p := &Phys{
		clients:     map[gordian.ClientId]clientData{},
		simTimer:    time.Tick(simTime),
		updateTimer: time.Tick(updateTime),
		Gordian:     gordian.New(0),
	}
	p.setup()
	return p
}

func (p *Phys) setup() {
	p.ballBodies = make([]chipmunk.Body, numBalls)
	p.ballShapes = make([]chipmunk.Shape, numBalls)
	p.ballList = make([]ballInfo, numBalls)
	p.box = make([]chipmunk.Shape, 4)
	p.space = chipmunk.SpaceNew()
	p.space.SetGravity(chipmunk.Vect{0, -100})

	radius, mass := 10.0, 1.0
	moment := chipmunk.MomentForCircle(mass, 0, radius, chipmunk.Origin())

	for i := range p.ballBodies {
		body := chipmunk.BodyNew(mass, moment)
		p.space.AddBody(body)
		p.ballBodies[i] = body
		shape := chipmunk.CircleShapeNew(body, radius, chipmunk.Origin())
		shape.SetFriction(0.5)
		shape.SetElasticity(0.9)
		shape.SetLayers(1)
		p.space.AddShape(shape)
		p.ballShapes[i] = shape
	}
	pts := []chipmunk.Vect{
		{-radius, -radius},
		{size + radius, -radius},
		{size + radius, size + radius},
		{-radius, size + radius},
	}
	for i := range p.box {
		shape := chipmunk.SegmentShapeNew(p.space.StaticBody(), pts[i],
			pts[(i+1)%len(pts)], radius)
		shape.SetElasticity(1.0)
		shape.SetFriction(1.0)
		p.space.AddShape(shape)
		p.box[i] = shape
	}
}

func (p *Phys) dropBalls() {
	for i := range p.ballBodies {
		radius := p.ballShapes[i].(chipmunk.CircleShape).Radius()
		p.ballBodies[i].SetPosition(chipmunk.Vect{0.5*size + (rand.Float64()*2-1)*radius,
			0.5*size + rand.Float64()*(0.5*size-2*radius)})
	}
}

func (p *Phys) Run() {
	go p.run()
	p.dropBalls()
	p.Gordian.Run()
}

func (p *Phys) run() {
	for {
		select {
		case client := <-p.Control:
			p.clientCtrl(client)
		case msg := <-p.InMessage:
			p.handleMessage(&msg)
		case <-p.simTimer:
			p.space.Step(float64(simTime) / float64(time.Second))
		case <-p.updateTimer:
			p.update()
		}
	}
}

func (p *Phys) clientCtrl(client *gordian.Client) {
	switch client.Ctrl {
	case gordian.CONNECT:
		p.connect(client)
	case gordian.CLOSE:
		p.close(client)
	}
}

func (p *Phys) connect(client *gordian.Client) {
	p.curId++
	client.Id = p.curId
	client.Ctrl = gordian.REGISTER
	body := chipmunk.BodyNew(math.Inf(0), math.Inf(0))
	p.clients[client.Id] = clientData{body: body}
	p.Control <- client
}

func (p *Phys) close(client *gordian.Client) {
	c, ok := p.clients[client.Id]
	if !ok {
		return
	}
	if c.joint != nil {
		p.space.RemoveConstraint(c.joint)
		c.joint.Free()
	}
	c.body.Free()
	delete(p.clients, client.Id)
}

func (p *Phys) handleMessage(msg *gordian.Message) {
	id := msg.From
	c, ok := p.clients[id]
	if !ok {
		return
	}

	rawData := msg.Data.(map[string]interface{})
	data := rawData["data"].(map[string]interface{})
	rawPos := data["pos"].(map[string]interface{})
	btn := data["btn"].(bool)
	pos := chipmunk.Vect{rawPos["x"].(float64), rawPos["y"].(float64)}
	c.body.SetPosition(pos)
	isDragging := (c.joint != nil)

	switch {
	case !isDragging && btn:
		shape := p.space.PointQueryFirst(pos, 1, chipmunk.NoGroup)
		if shape != nil {
			c.joint = chipmunk.PivotJointNew2(c.body, shape.Body(),
			chipmunk.Origin(), chipmunk.Origin())
			c.joint.SetMaxForce(10000.0)
			p.space.AddConstraint(c.joint)
		}
	case isDragging && !btn:
		if c.joint != nil {
			p.space.RemoveConstraint(c.joint)
			c.joint.Free()
			c.joint = nil
		}
	}
	p.clients[id] = c
}

func (p *Phys) update() {
	for i, bb := range p.ballBodies {
		pos := bb.Position()
		angle := bb.Angle()
		p.ballList[i] = ballInfo{pos, angle}
	}
	data := map[string]interface{}{}
	data["balls"] = p.ballList

	cursors := []chipmunk.Vect{}
	for _, client := range p.clients {
		cursors = append(cursors, client.body.Position())
	}

	payload := map[string]interface{}{}
	msg := gordian.Message{}
	data["cursors"] = cursors
	payload["type"] = "message"
	payload["data"] = data
	msg.Data = payload
	for id := range p.clients {
		msg.To = id
		p.OutMessage <- msg
	}
}
<|endoftext|>


<commit_before>package tfjson

import (
	"errors"
	"fmt"
)

// PlanFormatVersion is the version of the JSON plan format that is
// supported by this package.
const PlanFormatVersion = "0.1"

// ResourceMode is a string representation of the resource type found
// in certain fields in the plan.
type ResourceMode string

const (
	// DataResourceMode is the resource mode for data sources.
	DataResourceMode ResourceMode = "data"

	// ManagedResourceMode is the resource mode for managed resources.
	ManagedResourceMode ResourceMode = "managed"
)

// Plan represents the entire contents of an output Terraform plan.
type Plan struct {
	// The version of the plan format. This should always match the
	// PlanFormatVersion constant in this package, or else an unmarshal
	// will be unstable.
	FormatVersion string `json:"format_version,omitempty"`

	// The version of Terraform used to make the plan.
	TerraformVersion string `json:"terraform_version,omitempty"`

	// The variables set in the root module when creating the plan.
	Variables map[string]PlanVariable `json:"variables,omitempty"`

	// The common state representation of resources within this plan.
	// This is a product of the existing state merged with the diff for
	// this plan.
	PlannedValues *StateValues `json:"planned_values,omitempty"`

	// The change operations for resources and data sources within this
	// plan.
	ResourceChanges []ResourceChange `json:"resource_changes,omitempty"`

	// The change operations for outputs within this plan.
	OutputChanges map[string]Change `json:"output_changes,omitempty"`

	// The Terraform state prior to the plan operation. This is the
	// same format as PlannedValues, without the current diff merged.
	PriorState *State `json:"prior_state,omitempty"`

	// The Terraform configuration used to make the plan.
	Config *Config `json:"configuration,omitempty"`
}

// Validate checks to ensure that the plan is present, and the
// version matches the version supported by this library.
func (p *Plan) Validate() error {
	if p == nil {
		return errors.New("plan is nil")
	}

	if p.FormatVersion == "" {
		return errors.New("unexpected plan input, format version is missing")
	}

	if PlanFormatVersion != p.FormatVersion {
		return fmt.Errorf("unsupported plan format version: expected %q, got %q", PlanFormatVersion, p.FormatVersion)
	}

	return nil
}

// ResourceChange is a description of an individual change action
// that Terraform plans to use to move from the prior state to a new
// state matching the configuration.
type ResourceChange struct {
	// The absolute resource address.
	Address string `json:"address,omitempty"`

	// The module portion of the above address. Omitted if the instance
	// is in the root module.
	ModuleAddress string `json:"module_address,omitempty"`

	// The resource mode.
	Mode ResourceMode `json:"mode,omitempty"`

	// The resource type, example: "aws_instance" for aws_instance.foo.
	Type string `json:"type,omitempty"`

	// The resource name, example: "foo" for aws_instance.foo.
	Name string `json:"name,omitempty"`

	// The instance key for any resources that have been created using
	// "count" or "for_each". If neither of these apply the key will be
	// empty.
	//
	// This value can be either an integer (int) or a string.
	Index interface{} `json:"index,omitempty"`

	// If set, indicates that this action applies to a "deposed" object
	// of the given instance rather than to its "current" object.
	// Omitted for changes to the current object.
	Deposed bool `json:"deposed,omitempty"`

	// The data describing the change that will be made to this object.
	Change *Change `json:"change,omitempty"`
}

// Change is the representation of a proposed change for an object.
type Change struct {
	// The action to be carried out by this change.
	Actions Actions `json:"actions,omitempty"`

	// Before and After are representations of the object value both
	// before and after the action. For create and delete actions,
	// either Before or After is unset (respectively). For no-op
	// actions, both values will be identical. After will be incomplete
	// if there are values within it that won't be known until after
	// apply.
	Before interface{} `json:"before,"`
	After  interface{} `json:"after,omitempty"`

	// A deep object of booleans that denotes any values that are
	// unknown in a resource. These values were previously referred to
	// as "computed" values.
	//
	// If the value cannot be found in this map, then its value should
	// be available within After, so long as the operation supports it.
	AfterUnknown interface{} `json:"after_unknown,omitempty"`
}

// PlanVariable is a top-level variable in the Terraform plan.
type PlanVariable struct {
	// The value for this variable at plan time.
	Value interface{} `json:"value,omitempty"`
}
<commit_msg>plan: Deposed now DeposedKey, type now string<commit_after>package tfjson

import (
	"errors"
	"fmt"
)

// PlanFormatVersion is the version of the JSON plan format that is
// supported by this package.
const PlanFormatVersion = "0.1"

// ResourceMode is a string representation of the resource type found
// in certain fields in the plan.
type ResourceMode string

const (
	// DataResourceMode is the resource mode for data sources.
	DataResourceMode ResourceMode = "data"

	// ManagedResourceMode is the resource mode for managed resources.
	ManagedResourceMode ResourceMode = "managed"
)

// Plan represents the entire contents of an output Terraform plan.
type Plan struct {
	// The version of the plan format. This should always match the
	// PlanFormatVersion constant in this package, or else an unmarshal
	// will be unstable.
	FormatVersion string `json:"format_version,omitempty"`

	// The version of Terraform used to make the plan.
	TerraformVersion string `json:"terraform_version,omitempty"`

	// The variables set in the root module when creating the plan.
	Variables map[string]PlanVariable `json:"variables,omitempty"`

	// The common state representation of resources within this plan.
	// This is a product of the existing state merged with the diff for
	// this plan.
	PlannedValues *StateValues `json:"planned_values,omitempty"`

	// The change operations for resources and data sources within this
	// plan.
	ResourceChanges []ResourceChange `json:"resource_changes,omitempty"`

	// The change operations for outputs within this plan.
	OutputChanges map[string]Change `json:"output_changes,omitempty"`

	// The Terraform state prior to the plan operation. This is the
	// same format as PlannedValues, without the current diff merged.
	PriorState *State `json:"prior_state,omitempty"`

	// The Terraform configuration used to make the plan.
	Config *Config `json:"configuration,omitempty"`
}

// Validate checks to ensure that the plan is present, and the
// version matches the version supported by this library.
func (p *Plan) Validate() error {
	if p == nil {
		return errors.New("plan is nil")
	}

	if p.FormatVersion == "" {
		return errors.New("unexpected plan input, format version is missing")
	}

	if PlanFormatVersion != p.FormatVersion {
		return fmt.Errorf("unsupported plan format version: expected %q, got %q", PlanFormatVersion, p.FormatVersion)
	}

	return nil
}

// ResourceChange is a description of an individual change action
// that Terraform plans to use to move from the prior state to a new
// state matching the configuration.
type ResourceChange struct {
	// The absolute resource address.
	Address string `json:"address,omitempty"`

	// The module portion of the above address. Omitted if the instance
	// is in the root module.
	ModuleAddress string `json:"module_address,omitempty"`

	// The resource mode.
	Mode ResourceMode `json:"mode,omitempty"`

	// The resource type, example: "aws_instance" for aws_instance.foo.
	Type string `json:"type,omitempty"`

	// The resource name, example: "foo" for aws_instance.foo.
	Name string `json:"name,omitempty"`

	// The instance key for any resources that have been created using
	// "count" or "for_each". If neither of these apply the key will be
	// empty.
	//
	// This value can be either an integer (int) or a string.
	Index interface{} `json:"index,omitempty"`

	// DeposedKey is set during replace operations (that do not have
	// create_before_destroy set) and refers to the resource that this resource
	// is replacing.
	DeposedKey string `json:"deposed,omitempty"`

	// The data describing the change that will be made to this object.
	Change *Change `json:"change,omitempty"`
}

// Change is the representation of a proposed change for an object.
type Change struct {
	// The action to be carried out by this change.
	Actions Actions `json:"actions,omitempty"`

	// Before and After are representations of the object value both
	// before and after the action. For create and delete actions,
	// either Before or After is unset (respectively). For no-op
	// actions, both values will be identical. After will be incomplete
	// if there are values within it that won't be known until after
	// apply.
	Before interface{} `json:"before,"`
	After  interface{} `json:"after,omitempty"`

	// A deep object of booleans that denotes any values that are
	// unknown in a resource. These values were previously referred to
	// as "computed" values.
	//
	// If the value cannot be found in this map, then its value should
	// be available within After, so long as the operation supports it.
	AfterUnknown interface{} `json:"after_unknown,omitempty"`
}

// PlanVariable is a top-level variable in the Terraform plan.
type PlanVariable struct {
	// The value for this variable at plan time.
	Value interface{} `json:"value,omitempty"`
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"

	"golang.org/x/net/context"

	"github.com/docker/docker/api/client/bundlefile"
	"github.com/docker/docker/api/client/stack"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/filters"
	"github.com/docker/engine-api/types/swarm"
	"github.com/fatih/color"
	"github.com/urfave/cli"
)

var Replica1 uint64 = 1

type Services map[string]swarm.Service

func plan(c *cli.Context) error {
	stacks, err := getStacks(c)
	if err != nil {
		return err
	}

	detail := c.Bool("detail")
	target := c.StringSlice("target")
	targetMap := map[string]bool{}

	for _, name := range target {
		targetMap[name] = true
	}

	swarm, swarmErr := client.NewEnvClient()
	if swarmErr != nil {
		return cli.NewExitError(swarmErr.Error(), 3)
	}

	for _, stack := range stacks {
		filter := filters.NewArgs()
		filter.Add("label", "com.docker.stack.namespace="+stack.Name)
		services, servicesErr := swarm.ServiceList(context.Background(), types.ServiceListOptions{Filter: filter})
		if servicesErr != nil {
			return cli.NewExitError(servicesErr.Error(), 3)
		}

		expected := getBundleServicesSpec(stack.Bundle, stack.Name)
		translateNetworkToIds(&expected, swarm, stack.Name)

		current := getSwarmServicesSpecForStack(services)

		w := bufio.NewWriter(os.Stdout)
		sp := NewServicePrinter(w, detail)

		for n, es := range expected {
			// Only process found target services
			if _, found := targetMap[es.Spec.Name]; len(targetMap) == 0 || found {
				if cs, found := current[n]; !found {
					// New service to create
					color.Green("+ %s", n)
					sp.PrintServiceSpec(es.Spec)
					w.Flush()
					fmt.Println()
				} else {
					different := sp.PrintServiceSpecDiff(cs.Spec, es.Spec)
					if different {
						color.Yellow("~ %s\n", es.Spec.Name)
					} else if detail {
						color.Cyan("%s\n", es.Spec.Name)
					}
					w.Flush()
					fmt.Println()
				}
			}
		}

		// Checks services to remove
		for n, cs := range current {
			// Only process found target services
			if _, found := targetMap[cs.Spec.Name]; len(targetMap) == 0 || found {
				if _, found := expected[n]; !found {
					color.Red("- %s", n)
					sp.PrintServiceSpec(cs.Spec)
					w.Flush()
					fmt.Println()
				}
			}

		}
	}

	return nil
}

func safeDereference(p *string) string {
	if p == nil {
		return ""
	}
	return *p
}

func translateNetworkToIds(services *Services, cli *client.Client, stackName string) {
	existingNetworks, err := stack.GetNetworks(context.Background(), cli, stackName)
	if err != nil {
		log.Fatal("Error retrieving networks")
	}

	for _, service := range *services {
		for i, network := range service.Spec.Networks {
			for _, enet := range existingNetworks {
				if enet.Name == network.Target {
					service.Spec.Networks[i].Target = enet.ID
					network.Target = enet.ID
				}
			}
		}
	}
}

func getBundleServicesSpec(bundle *bundlefile.Bundlefile, stackName string) Services {
	specs := Services{}

	for name, service := range bundle.Services {
		spec := swarm.ServiceSpec{
			TaskTemplate: swarm.TaskSpec{
				ContainerSpec: swarm.ContainerSpec{
					Image:   service.Image,
					Labels:  service.Labels,
					Command: service.Command,
					Args:    service.Args,
					Env:     service.Env,
					Dir:     safeDereference(service.WorkingDir),
					User:    safeDereference(service.User),
				},
				Placement: &swarm.Placement{Constraints: service.Constraints},
			},
			Mode: swarm.ServiceMode{
				Replicated: &swarm.ReplicatedService{
					Replicas: service.Replicas,
				},
			},
			Networks: convertNetworks(service.Networks, stackName, name),
		}

		spec.Mode = swarm.ServiceMode{
			Replicated: &swarm.ReplicatedService{
				Replicas: &Replica1,
			},
		}

		if service.Replicas != nil {
			spec.Mode.Replicated.Replicas = service.Replicas
		}

		spec.Labels = map[string]string{"com.docker.stack.namespace": stackName}
		spec.Name = fmt.Sprintf("%s_%s", stackName, name)

		// Populate ports
		ports := []swarm.PortConfig{}
		for _, port := range service.Ports {
			p := swarm.PortConfig{
				TargetPort:    port.Port,
				Protocol:      swarm.PortConfigProtocol(port.Protocol),
				PublishedPort: port.PublishedPort,
			}

			ports = append(ports, p)
		}
		// Hardcode resolution mode to VIP as it's the default with dab
		mode := "vip"
		if service.EndpointMode != nil {
			if *service.EndpointMode != "dnsrr" && *service.EndpointMode != "vip" {
				log.Fatalf("Invalid mode \"%s\" for service %s, only \"dnsrr\" or \"vip\" is allowed", *service.EndpointMode, spec.Name)
			}
			mode = *service.EndpointMode
		}
		spec.EndpointSpec = &swarm.EndpointSpec{Ports: ports, Mode: swarm.ResolutionMode(mode)}

		service := swarm.Service{}
		service.ID = spec.Name
		service.Spec = spec

		specs[spec.Name] = service
	}
	return specs
}

func convertNetworks(networks []string, namespace string, name string) []swarm.NetworkAttachmentConfig {
	nets := []swarm.NetworkAttachmentConfig{}
	for _, network := range networks {
		nets = append(nets, swarm.NetworkAttachmentConfig{
			Target:  namespace + "_" + network,
			Aliases: []string{name},
		})
	}
	return nets
}

func getSwarmServicesSpecForStack(services []swarm.Service) Services {
	specs := Services{}

	for _, service := range services {
		specs[service.Spec.Name] = service
	}

	return specs
}
<commit_msg>Flush results only if there's anything to show<commit_after>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"

	"golang.org/x/net/context"

	"github.com/docker/docker/api/client/bundlefile"
	"github.com/docker/docker/api/client/stack"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/filters"
	"github.com/docker/engine-api/types/swarm"
	"github.com/fatih/color"
	"github.com/urfave/cli"
)

var Replica1 uint64 = 1

type Services map[string]swarm.Service

func plan(c *cli.Context) error {
	stacks, err := getStacks(c)
	if err != nil {
		return err
	}

	detail := c.Bool("detail")
	target := c.StringSlice("target")
	targetMap := map[string]bool{}

	for _, name := range target {
		targetMap[name] = true
	}

	swarm, swarmErr := client.NewEnvClient()
	if swarmErr != nil {
		return cli.NewExitError(swarmErr.Error(), 3)
	}

	for _, stack := range stacks {
		filter := filters.NewArgs()
		filter.Add("label", "com.docker.stack.namespace="+stack.Name)
		services, servicesErr := swarm.ServiceList(context.Background(), types.ServiceListOptions{Filter: filter})
		if servicesErr != nil {
			return cli.NewExitError(servicesErr.Error(), 3)
		}

		expected := getBundleServicesSpec(stack.Bundle, stack.Name)
		translateNetworkToIds(&expected, swarm, stack.Name)

		current := getSwarmServicesSpecForStack(services)

		w := bufio.NewWriter(os.Stdout)
		sp := NewServicePrinter(w, detail)

		for n, es := range expected {
			// Only process found target services
			if _, found := targetMap[es.Spec.Name]; len(targetMap) == 0 || found {
				if cs, found := current[n]; !found {
					// New service to create
					color.Green("+ %s", n)
					sp.PrintServiceSpec(es.Spec)
					w.Flush()
					fmt.Println()
				} else {
					different := sp.PrintServiceSpecDiff(cs.Spec, es.Spec)
					if different {
						color.Yellow("~ %s\n", es.Spec.Name)
					} else if detail {
						color.Cyan("%s\n", es.Spec.Name)
					}

					// flush if results
					if different || detail {
						w.Flush()
						fmt.Println()
					}
				}
			}
		}

		// Checks services to remove
		for n, cs := range current {
			// Only process found target services
			if _, found := targetMap[cs.Spec.Name]; len(targetMap) == 0 || found {
				if _, found := expected[n]; !found {
					color.Red("- %s", n)
					sp.PrintServiceSpec(cs.Spec)
					w.Flush()
					fmt.Println()
				}
			}

		}
	}

	return nil
}

func safeDereference(p *string) string {
	if p == nil {
		return ""
	}
	return *p
}

func translateNetworkToIds(services *Services, cli *client.Client, stackName string) {
	existingNetworks, err := stack.GetNetworks(context.Background(), cli, stackName)
	if err != nil {
		log.Fatal("Error retrieving networks")
	}

	for _, service := range *services {
		for i, network := range service.Spec.Networks {
			for _, enet := range existingNetworks {
				if enet.Name == network.Target {
					service.Spec.Networks[i].Target = enet.ID
					network.Target = enet.ID
				}
			}
		}
	}
}

func getBundleServicesSpec(bundle *bundlefile.Bundlefile, stackName string) Services {
	specs := Services{}

	for name, service := range bundle.Services {
		spec := swarm.ServiceSpec{
			TaskTemplate: swarm.TaskSpec{
				ContainerSpec: swarm.ContainerSpec{
					Image:   service.Image,
					Labels:  service.Labels,
					Command: service.Command,
					Args:    service.Args,
					Env:     service.Env,
					Dir:     safeDereference(service.WorkingDir),
					User:    safeDereference(service.User),
				},
				Placement: &swarm.Placement{Constraints: service.Constraints},
			},
			Mode: swarm.ServiceMode{
				Replicated: &swarm.ReplicatedService{
					Replicas: service.Replicas,
				},
			},
			Networks: convertNetworks(service.Networks, stackName, name),
		}

		spec.Mode = swarm.ServiceMode{
			Replicated: &swarm.ReplicatedService{
				Replicas: &Replica1,
			},
		}

		if service.Replicas != nil {
			spec.Mode.Replicated.Replicas = service.Replicas
		}

		spec.Labels = map[string]string{"com.docker.stack.namespace": stackName}
		spec.Name = fmt.Sprintf("%s_%s", stackName, name)

		// Populate ports
		ports := []swarm.PortConfig{}
		for _, port := range service.Ports {
			p := swarm.PortConfig{
				TargetPort:    port.Port,
				Protocol:      swarm.PortConfigProtocol(port.Protocol),
				PublishedPort: port.PublishedPort,
			}

			ports = append(ports, p)
		}
		// Hardcode resolution mode to VIP as it's the default with dab
		mode := "vip"
		if service.EndpointMode != nil {
			if *service.EndpointMode != "dnsrr" && *service.EndpointMode != "vip" {
				log.Fatalf("Invalid mode \"%s\" for service %s, only \"dnsrr\" or \"vip\" is allowed", *service.EndpointMode, spec.Name)
			}
			mode = *service.EndpointMode
		}
		spec.EndpointSpec = &swarm.EndpointSpec{Ports: ports, Mode: swarm.ResolutionMode(mode)}

		service := swarm.Service{}
		service.ID = spec.Name
		service.Spec = spec

		specs[spec.Name] = service
	}
	return specs
}

func convertNetworks(networks []string, namespace string, name string) []swarm.NetworkAttachmentConfig {
	nets := []swarm.NetworkAttachmentConfig{}
	for _, network := range networks {
		nets = append(nets, swarm.NetworkAttachmentConfig{
			Target:  namespace + "_" + network,
			Aliases: []string{name},
		})
	}
	return nets
}

func getSwarmServicesSpecForStack(services []swarm.Service) Services {
	specs := Services{}

	for _, service := range services {
		specs[service.Spec.Name] = service
	}

	return specs
}
<|endoftext|>


<commit_before>package pongo2

import (
	"bytes"
	"fmt"
	"io"
)

type Template struct {
	set *TemplateSet

	// Input
	is_tpl_string bool
	name          string
	tpl           string
	size          int

	// Calculation
	tokens []*Token
	parser *Parser

	// first come, first serve (it's important to not override existing entries in here)
	level           int
	parent          *Template
	child           *Template
	blocks          map[string]*NodeWrapper
	exported_macros map[string]*tagMacroNode

	// Output
	root *nodeDocument
}

func newTemplateString(set *TemplateSet, tpl string) (*Template, error) {
	return newTemplate(set, "<string>", true, tpl)
}

func newTemplate(set *TemplateSet, name string, is_tpl_string bool, tpl string) (*Template, error) {
	// Create the template
	t := &Template{
		set:             set,
		is_tpl_string:   is_tpl_string,
		name:            name,
		tpl:             tpl,
		size:            len(tpl),
		blocks:          make(map[string]*NodeWrapper),
		exported_macros: make(map[string]*tagMacroNode),
	}

	// Tokenize it
	tokens, err := lex(name, tpl)
	if err != nil {
		return nil, err
	}
	t.tokens = tokens

	// For debugging purposes, show all tokens:
	/*for i, t := range tokens {
		fmt.Printf("%3d. %s\n", i, t)
	}*/

	// Parse it
	err = t.parse()
	if err != nil {
		return nil, err
	}

	return t, nil
}

func (tpl *Template) execute(context Context) (*bytes.Buffer, error) {
	// Create output buffer
	// We assume that the rendered template will be 30% larger
	buffer := bytes.NewBuffer(make([]byte, 0, int(float64(tpl.size)*1.3)))

	// Determine the parent to be executed (for template inheritance)
	parent := tpl
	for parent.parent != nil {
		parent = parent.parent
	}

	// Create context if none is given
	newContext := make(Context)
	newContext.Update(tpl.set.Globals)

	if context != nil {
		newContext.Update(context)

		if len(newContext) > 0 {
			// Check for context name syntax
			err := newContext.checkForValidIdentifiers()
			if err != nil {
				return nil, err
			}

			// Check for clashes with macro names
			for k, _ := range newContext {
				_, has := tpl.exported_macros[k]
				if has {
					return nil, &Error{
						Filename: tpl.name,
						Sender:   "execution",
						ErrorMsg: fmt.Sprintf("Context key name '%s' clashes with macro '%s'.", k, k),
					}
				}
			}
		}
	}

	// Create operational context
	ctx := newExecutionContext(parent, newContext)

	// Run the selected document
	err := parent.root.Execute(ctx, buffer)
	if err != nil {
		return nil, err
	}
	return buffer, nil
}

// Executes the template with the given context and writes to writer (io.Writer)
// on success. Context can be nil. Nothing is written on error; instead the error
// is being returned.
func (tpl *Template) ExecuteWriter(context Context, writer io.Writer) error {
	buffer, err := tpl.execute(context)
	if err != nil {
		return err
	}

	l := buffer.Len()
	n, werr := buffer.WriteTo(writer)
	if int(n) != l {
		panic(fmt.Sprintf("error on writing template: n(%d) != buffer.Len(%d)", n, l))
	}
	if werr != nil {
		return &Error{
			Filename: tpl.name,
			Sender:   "execution",
			ErrorMsg: werr.Error(),
		}
	}
	return nil
}

// Executes the template and returns the rendered template as a []byte
func (tpl *Template) ExecuteBytes(context Context) ([]byte, error) {
	// Execute template
	buffer, err := tpl.execute(context)
	if err != nil {
		return nil, err
	}
	return buffer.Bytes(), nil
}

// Executes the template and returns the rendered template as a string
func (tpl *Template) Execute(context Context) (string, error) {
	// Execute template
	buffer, err := tpl.execute(context)
	if err != nil {
		return "", err
	}

	return buffer.String(), nil

}
<commit_msg>ExecuteBuffer for that case if you already know the size of your rendered template.<commit_after>package pongo2

import (
	"bytes"
	"fmt"
	"io"
)

type Template struct {
	set *TemplateSet

	// Input
	is_tpl_string bool
	name          string
	tpl           string
	size          int

	// Calculation
	tokens []*Token
	parser *Parser

	// first come, first serve (it's important to not override existing entries in here)
	level           int
	parent          *Template
	child           *Template
	blocks          map[string]*NodeWrapper
	exported_macros map[string]*tagMacroNode

	// Output
	root *nodeDocument
}

func newTemplateString(set *TemplateSet, tpl string) (*Template, error) {
	return newTemplate(set, "<string>", true, tpl)
}

func newTemplate(set *TemplateSet, name string, is_tpl_string bool, tpl string) (*Template, error) {
	// Create the template
	t := &Template{
		set:             set,
		is_tpl_string:   is_tpl_string,
		name:            name,
		tpl:             tpl,
		size:            len(tpl),
		blocks:          make(map[string]*NodeWrapper),
		exported_macros: make(map[string]*tagMacroNode),
	}

	// Tokenize it
	tokens, err := lex(name, tpl)
	if err != nil {
		return nil, err
	}
	t.tokens = tokens

	// For debugging purposes, show all tokens:
	/*for i, t := range tokens {
		fmt.Printf("%3d. %s\n", i, t)
	}*/

	// Parse it
	err = t.parse()
	if err != nil {
		return nil, err
	}

	return t, nil
}

func (tpl *Template) execute(context Context) (*bytes.Buffer, error) {
	// Create output buffer
	// We assume that the rendered template will be 30% larger
	buffer := bytes.NewBuffer(make([]byte, 0, int(float64(tpl.size)*1.3)))
	if err := tpl.executeBuffer(context, buffer); err != nil {
		return nil, err
	}
	return buffer, nil
}

func (tpl *Template) executeBuffer(context Context, buffer *bytes.Buffer) error {
	// Determine the parent to be executed (for template inheritance)
	parent := tpl
	for parent.parent != nil {
		parent = parent.parent
	}

	// Create context if none is given
	newContext := make(Context)
	newContext.Update(tpl.set.Globals)

	if context != nil {
		newContext.Update(context)

		if len(newContext) > 0 {
			// Check for context name syntax
			err := newContext.checkForValidIdentifiers()
			if err != nil {
				return err
			}

			// Check for clashes with macro names
			for k, _ := range newContext {
				_, has := tpl.exported_macros[k]
				if has {
					return &Error{
						Filename: tpl.name,
						Sender:   "execution",
						ErrorMsg: fmt.Sprintf("Context key name '%s' clashes with macro '%s'.", k, k),
					}
				}
			}
		}
	}

	// Create operational context
	ctx := newExecutionContext(parent, newContext)

	// Run the selected document
	if err := parent.root.Execute(ctx, buffer); err != nil {
		return err
	}

	return nil
}

// Executes the template with the given context and writes to writer (io.Writer)
// on success. Context can be nil. Nothing is written on error; instead the error
// is being returned.
func (tpl *Template) ExecuteWriter(context Context, writer io.Writer) error {
	buffer, err := tpl.execute(context)
	if err != nil {
		return err
	}

	l := buffer.Len()
	n, werr := buffer.WriteTo(writer)
	if int(n) != l {
		panic(fmt.Sprintf("error on writing template: n(%d) != buffer.Len(%d)", n, l))
	}
	if werr != nil {
		return &Error{
			Filename: tpl.name,
			Sender:   "execution",
			ErrorMsg: werr.Error(),
		}
	}
	return nil
}

// Executes the template with the given context and writes to buffer (bytes.Buffer)
// on success. This method can use useful if you already have predefined buffer with
// specified size.
func (tpl *Template) ExecuteBuffer(context Context, buffer *bytes.Buffer) error {
	return tpl.executeBuffer(context, buffer)
}

// Executes the template and returns the rendered template as a []byte
func (tpl *Template) ExecuteBytes(context Context) ([]byte, error) {
	// Execute template
	buffer, err := tpl.execute(context)
	if err != nil {
		return nil, err
	}
	return buffer.Bytes(), nil
}

// Executes the template and returns the rendered template as a string
func (tpl *Template) Execute(context Context) (string, error) {
	// Execute template
	buffer, err := tpl.execute(context)
	if err != nil {
		return "", err
	}

	return buffer.String(), nil

}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"os/exec"
	"runtime"
	"strings"

	termbox "github.com/nsf/termbox-go"
)

type Terminal struct {
	Links         []Link
	ViewFullURL   bool
	ViewFullHelp  bool
	Selected      int
	Width, Height int
}

const (
	help_mini string = "h: help   q: quit"
	help_full string = `

h:           toggle help (press again to return to menu)
tab:         toggle full url
g:           go to top
G:           go to bottom
j/C-n:       move down
k/C-p:       move up
return/C-o:  open url
q/C-c:       quit`
)

var (
	EventChan    = make(chan termbox.Event)
	KeyTab       = termbox.KeyTab
	KeyEnter     = termbox.KeyEnter
	KeyArrowUp   = termbox.KeyArrowUp
	KeyArrowDown = termbox.KeyArrowDown
	KeyCtrlP     = termbox.KeyCtrlP
	KeyCtrlN     = termbox.KeyCtrlN
	KeyCtrlO     = termbox.KeyCtrlO
	KeyCtrlC     = termbox.KeyCtrlC
)

func PollEvent() {
	EventChan <- termbox.PollEvent()
}

func NewTerminal(links *[]Link) *Terminal {
	term := Terminal{
		Links:    *links,
		Selected: 0,
		Width:    0,
		Height:   0,
	}

	return &term
}

func (t *Terminal) Start() error {
	err := termbox.Init()
	if err != nil {
		return err
	}

	return nil
}

func (t *Terminal) Close() {
	termbox.Close()
}

func (t *Terminal) HandleEvent(e termbox.Event) (bool, error) {
	if e.Type == termbox.EventResize {
		t.SetSize()
		t.Render()
	}

	var err error
	if e.Type == termbox.EventKey {
		if e.Ch == 0 {
			switch e.Key {
			case KeyArrowDown:
				t.MoveSelection("down")
				t.Render()
			case KeyCtrlN:
				t.MoveSelection("down")
				t.Render()
			case KeyArrowUp:
				t.MoveSelection("up")
				t.Render()
			case KeyCtrlP:
				t.MoveSelection("up")
				t.Render()
			case KeyTab:
				if !t.ViewFullURL {
					t.ShowFullLink()
				} else {
					t.Render()
					t.ViewFullURL = false
				}
			case KeyEnter:
				err = t.Select()
			case KeyCtrlO:
				err = t.Select()
			case KeyCtrlC:
				return true, nil
			}
		} else {
			switch e.Ch {
			case 'G':
				t.GoToBottom()
				t.Render()
			case 'g':
				t.GoToTop()
				t.Render()
			case 'j':
				t.MoveSelection("down")
				t.Render()
			case 'k':
				t.MoveSelection("up")
				t.Render()
			case 'h':
				if !t.ViewFullHelp {
					t.ShowFullHelp()
				} else {
					t.Render()
					t.ViewFullHelp = false
				}
			case 'q':
				return true, nil
			}
		}
	}

	return false, err
}

func (t *Terminal) Println(x int, y int, s string) {
	for col, char := range s {
		termbox.SetCell(col+x, y, char, termbox.ColorDefault, termbox.ColorDefault)
	}
}

func (t *Terminal) PrintHeader() {
	t.Println(0, 0, help_mini)
	t.Println(len(help_mini)+3, 0, fmt.Sprintf("(%d of %d)", t.Selected+1, len(t.Links)))
}

func (t *Terminal) ShowFullHelp() {
	t.ViewFullHelp = true
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

	t.Println(0, 0, help_mini)
	scanner := bufio.NewScanner(strings.NewReader(help_full))
	row := 0
	for scanner.Scan() {
		t.Println(0, row, scanner.Text())
		row++
	}

	termbox.Flush()
}

func (t *Terminal) ShowFullLink() {
	t.ViewFullURL = true
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

	t.PrintHeader()

	url := t.Links[t.Selected].URL
	row := 2
	col := 0
	for _, char := range url {
		if col >= t.Width {
			row++
			col = 0
		}
		termbox.SetCell(col, row, char, termbox.ColorDefault, termbox.ColorDefault)
		col++
	}

	termbox.Flush()
}

func (t *Terminal) GoToTop() {
	t.Selected = 0
}

func (t *Terminal) GoToBottom() {
	t.Selected = len(t.Links) - 1
}

func (t *Terminal) Render() {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

	t.PrintHeader()

	url := t.Links[t.Selected].URL
	t.Println(0, 2, url)

	var start int
	offset := t.Selected - t.Height + 6
	if t.Selected > t.Height-6 {
		start = offset
	}

	for i := start; i < len(t.Links); i++ {
		if t.Selected > t.Height-6 {
			t.Println(0, t.Height-2, "->")
			t.Println(3, i+4-offset, t.Links[i].Text)
		} else {
			t.Println(0, t.Selected+4, "->")
			t.Println(3, i+4, t.Links[i].Text)
		}
	}

	termbox.Flush()
}

func (t *Terminal) SetSize() {
	t.Width, t.Height = termbox.Size()
}

func (t *Terminal) MoveSelection(direction string) {
	switch direction {
	case "up":
		t.Selected--
	case "down":
		t.Selected++
	}

	if t.Selected >= len(t.Links) {
		t.Selected = len(t.Links) - 1
	}

	if t.Selected < 0 {
		t.Selected = 0
	}
}

func (t *Terminal) Select() error {
	var err error
	url := t.Links[t.Selected].URL

	switch runtime.GOOS {
	case "linux":
		err = exec.Command("xdg-open", url).Start()
	case "windows":
		err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
	case "darwin":
		err = exec.Command("open", url).Start()
	default:
		err = fmt.Errorf("can't open browser: unsupported platform")
	}

	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Use enums instead of silly strings for direction<commit_after>package main

import (
	"bufio"
	"fmt"
	"os/exec"
	"runtime"
	"strings"

	termbox "github.com/nsf/termbox-go"
)

type Terminal struct {
	Links         []Link
	ViewFullURL   bool
	ViewFullHelp  bool
	Selected      int
	Width, Height int
}

const (
	UP = iota
	DOWN

	help_mini string = "h: help   q: quit"
	help_full string = `

h:           toggle help (press again to return to menu)
tab:         toggle full url
g:           go to top
G:           go to bottom
j/C-n:       move down
k/C-p:       move up
return/C-o:  open url
q/C-c:       quit`
)

var (
	EventChan    = make(chan termbox.Event)
	KeyTab       = termbox.KeyTab
	KeyEnter     = termbox.KeyEnter
	KeyArrowUp   = termbox.KeyArrowUp
	KeyArrowDown = termbox.KeyArrowDown
	KeyCtrlP     = termbox.KeyCtrlP
	KeyCtrlN     = termbox.KeyCtrlN
	KeyCtrlO     = termbox.KeyCtrlO
	KeyCtrlC     = termbox.KeyCtrlC
)

func PollEvent() {
	EventChan <- termbox.PollEvent()
}

func NewTerminal(links *[]Link) *Terminal {
	term := Terminal{
		Links:    *links,
		Selected: 0,
		Width:    0,
		Height:   0,
	}

	return &term
}

func (t *Terminal) Start() error {
	err := termbox.Init()
	if err != nil {
		return err
	}

	return nil
}

func (t *Terminal) Close() {
	termbox.Close()
}

func (t *Terminal) HandleEvent(e termbox.Event) (bool, error) {
	if e.Type == termbox.EventResize {
		t.SetSize()
		t.Render()
	}

	var err error
	if e.Type == termbox.EventKey {
		if e.Ch == 0 {
			switch e.Key {
			case KeyArrowDown:
				t.MoveSelection(DOWN)
				t.Render()
			case KeyCtrlN:
				t.MoveSelection(DOWN)
				t.Render()
			case KeyArrowUp:
				t.MoveSelection(UP)
				t.Render()
			case KeyCtrlP:
				t.MoveSelection(UP)
				t.Render()
			case KeyTab:
				if !t.ViewFullURL {
					t.ShowFullLink()
				} else {
					t.Render()
					t.ViewFullURL = false
				}
			case KeyEnter:
				err = t.Select()
			case KeyCtrlO:
				err = t.Select()
			case KeyCtrlC:
				return true, nil
			}
		} else {
			switch e.Ch {
			case 'G':
				t.GoToBottom()
				t.Render()
			case 'g':
				t.GoToTop()
				t.Render()
			case 'j':
				t.MoveSelection(DOWN)
				t.Render()
			case 'k':
				t.MoveSelection(UP)
				t.Render()
			case 'h':
				if !t.ViewFullHelp {
					t.ShowFullHelp()
				} else {
					t.Render()
					t.ViewFullHelp = false
				}
			case 'q':
				return true, nil
			}
		}
	}

	return false, err
}

func (t *Terminal) Println(x int, y int, s string) {
	for col, char := range s {
		termbox.SetCell(col+x, y, char, termbox.ColorDefault, termbox.ColorDefault)
	}
}

func (t *Terminal) PrintHeader() {
	t.Println(0, 0, help_mini)
	t.Println(len(help_mini)+3, 0, fmt.Sprintf("(%d of %d)", t.Selected+1, len(t.Links)))
}

func (t *Terminal) ShowFullHelp() {
	t.ViewFullHelp = true
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

	t.Println(0, 0, help_mini)
	scanner := bufio.NewScanner(strings.NewReader(help_full))
	row := 0
	for scanner.Scan() {
		t.Println(0, row, scanner.Text())
		row++
	}

	termbox.Flush()
}

func (t *Terminal) ShowFullLink() {
	t.ViewFullURL = true
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

	t.PrintHeader()

	url := t.Links[t.Selected].URL
	row := 2
	col := 0
	for _, char := range url {
		if col >= t.Width {
			row++
			col = 0
		}
		termbox.SetCell(col, row, char, termbox.ColorDefault, termbox.ColorDefault)
		col++
	}

	termbox.Flush()
}

func (t *Terminal) Render() {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

	t.PrintHeader()

	url := t.Links[t.Selected].URL
	t.Println(0, 2, url)

	var start int
	offset := t.Selected - t.Height + 6
	if t.Selected > t.Height-6 {
		start = offset
	}

	for i := start; i < len(t.Links); i++ {
		if t.Selected > t.Height-6 {
			t.Println(0, t.Height-2, "->")
			t.Println(3, i+4-offset, t.Links[i].Text)
		} else {
			t.Println(0, t.Selected+4, "->")
			t.Println(3, i+4, t.Links[i].Text)
		}
	}

	termbox.Flush()
}

func (t *Terminal) SetSize() {
	t.Width, t.Height = termbox.Size()
}

func (t *Terminal) MoveSelection(direction int) {
	switch direction {
	case 0:
		t.Selected--
	case 1:
		t.Selected++
	}

	if t.Selected >= len(t.Links) {
		t.Selected = len(t.Links) - 1
	}

	if t.Selected < 0 {
		t.Selected = 0
	}
}

func (t *Terminal) GoToTop() {
	t.Selected = 0
}

func (t *Terminal) GoToBottom() {
	t.Selected = len(t.Links) - 1
}

func (t *Terminal) Select() error {
	var err error
	url := t.Links[t.Selected].URL

	switch runtime.GOOS {
	case "linux":
		err = exec.Command("xdg-open", url).Start()
	case "windows":
		err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
	case "darwin":
		err = exec.Command("open", url).Start()
	default:
		err = fmt.Errorf("can't open browser: unsupported platform")
	}

	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package ssh

import (
	"fmt"
	"strings"

	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

// Structure to hold roles that are allowed to accept any IP address.
type zeroAddressRoles struct {
	Roles []string `json:"roles"`
}

func pathConfigZeroAddress(b *backend) *framework.Path {
	return &framework.Path{
		Pattern: "config/zeroaddress",
		Fields: map[string]*framework.FieldSchema{
			"roles": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `[Required] Comma separated list of role names which
				allows credentials to be requested for any IP address. CIDR blocks
				previously registered under these roles will be ignored.`,
			},
		},
		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.WriteOperation: b.pathConfigZeroAddressWrite,
			logical.ReadOperation:  b.pathConfigZeroAddressRead,
		},
		HelpSynopsis:    pathConfigZeroAddressSyn,
		HelpDescription: pathConfigZeroAddressDesc,
	}
}

func (b *backend) pathConfigZeroAddressRead(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	entry, err := b.getZeroAddressRoles(req.Storage)
	if err != nil {
		return nil, err
	}
	if entry == nil {
		return nil, nil
	}

	return &logical.Response{
		Data: map[string]interface{}{
			"roles": entry.Roles,
		},
	}, nil
}

func (b *backend) pathConfigZeroAddressWrite(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	roleNames := d.Get("roles").(string)
	if roleNames == "" {
		return logical.ErrorResponse("Missing roles"), nil
	}

	// Check if the roles listed actually exist in the backend
	roles := strings.Split(roleNames, ",")
	for _, item := range roles {
		role, err := b.getRole(req.Storage, item)
		if err != nil {
			return nil, err
		}
		if role == nil {
			return logical.ErrorResponse(fmt.Sprintf("Role [%s] does not exist", item)), nil
		}
	}

	err := b.putZeroAddressRoles(req.Storage, roles)
	if err != nil {
		return nil, err
	}

	return nil, nil
}

// Stores the given list of roles at zeroaddress endpoint
func (b *backend) putZeroAddressRoles(s logical.Storage, roles []string) error {
	entry, err := logical.StorageEntryJSON("config/zeroaddress", &zeroAddressRoles{
		Roles: roles,
	})
	if err != nil {
		return err
	}
	if err := s.Put(entry); err != nil {
		return err
	}
	return nil
}

// Retrieves the list of roles from the zeroaddress endpoint.
func (b *backend) getZeroAddressRoles(s logical.Storage) (*zeroAddressRoles, error) {
	entry, err := s.Get("config/zeroaddress")
	if err != nil {
		return nil, err
	}
	if entry == nil {
		return nil, nil
	}

	var result zeroAddressRoles
	if err := entry.DecodeJSON(&result); err != nil {
		return nil, err
	}

	return &result, nil
}

func (b *backend) removeZeroAddressRole(s logical.Storage, roleName string) error {
	zeroAddressEntry, err := b.getZeroAddressRoles(s)
	if err != nil {
		return err
	}
	if zeroAddressEntry == nil {
		return nil
	}

	err = zeroAddressEntry.Remove(roleName)
	if err != nil {
		return err
	}

	return b.putZeroAddressRoles(s, zeroAddressEntry.Roles)
}

func (r *zeroAddressRoles) Remove(roleName string) error {
	var index int
	for i, role := range r.Roles {
		if role == roleName {
			index = i
			break
		}
	}
	length := len(r.Roles)
	if index >= length || index < 0 {
		return fmt.Errorf("invalid index [%d]", index)
	}
	// If slice has zero or one item, remove the item by setting slice to nil.
	if length < 2 {
		r.Roles = nil
		return nil
	}

	// Last item to be deleted
	if length-1 == index {
		r.Roles = r.Roles[:length-1]
		return nil
	}

	// Delete the item by appending all items except the one at index
	r.Roles = append(r.Roles[:index], r.Roles[index+1:]...)
	return nil
}

const pathConfigZeroAddressSyn = `
Assign zero address as default CIDR block for select roles.
`

const pathConfigZeroAddressDesc = `
Administrator can choose to make a select few registered roles to accept any IP
address, overriding the CIDR blocks registered during creation of roles. This
doesn't mean that the credentials are created for any IP address. Clients who
have access to these roles are trusted to make valid requests. Access to these
roles should be controlled using Vault policies. It is recommended that all the
roles that are allowed to accept any IP address should have an explicit policy
of deny for unintended clients.

This is a root authenticated endpoint. If backend is mounted at 'ssh' then use
the endpoint 'ssh/config/zeroaddress' to provide the list of allowed roles.
After mounting the backend, use 'path-help' for additional information.
`
<commit_msg>Vault SSH: Add read method for zeroaddress endpoint<commit_after>package ssh

import (
	"fmt"
	"strings"

	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

// Structure to hold roles that are allowed to accept any IP address.
type zeroAddressRoles struct {
	Roles []string `json:"roles" mapstructure:"roles"`
}

func pathConfigZeroAddress(b *backend) *framework.Path {
	return &framework.Path{
		Pattern: "config/zeroaddress",
		Fields: map[string]*framework.FieldSchema{
			"roles": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `[Required] Comma separated list of role names which
				allows credentials to be requested for any IP address. CIDR blocks
				previously registered under these roles will be ignored.`,
			},
		},
		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.WriteOperation:  b.pathConfigZeroAddressWrite,
			logical.ReadOperation:   b.pathConfigZeroAddressRead,
			logical.DeleteOperation: b.pathConfigZeroAddressDelete,
		},
		HelpSynopsis:    pathConfigZeroAddressSyn,
		HelpDescription: pathConfigZeroAddressDesc,
	}
}

func (b *backend) pathConfigZeroAddressDelete(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	err := req.Storage.Delete("config/zeroaddress")
	if err != nil {
		return nil, err
	}
	return nil, nil
}

func (b *backend) pathConfigZeroAddressRead(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	entry, err := b.getZeroAddressRoles(req.Storage)
	if err != nil {
		return nil, err
	}
	if entry == nil {
		return nil, nil
	}

	return &logical.Response{
		Data: map[string]interface{}{
			"roles": entry.Roles,
		},
	}, nil
}

func (b *backend) pathConfigZeroAddressWrite(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	roleNames := d.Get("roles").(string)
	if roleNames == "" {
		return logical.ErrorResponse("Missing roles"), nil
	}

	// Check if the roles listed actually exist in the backend
	roles := strings.Split(roleNames, ",")
	for _, item := range roles {
		role, err := b.getRole(req.Storage, item)
		if err != nil {
			return nil, err
		}
		if role == nil {
			return logical.ErrorResponse(fmt.Sprintf("Role [%s] does not exist", item)), nil
		}
	}

	err := b.putZeroAddressRoles(req.Storage, roles)
	if err != nil {
		return nil, err
	}

	return nil, nil
}

// Stores the given list of roles at zeroaddress endpoint
func (b *backend) putZeroAddressRoles(s logical.Storage, roles []string) error {
	entry, err := logical.StorageEntryJSON("config/zeroaddress", &zeroAddressRoles{
		Roles: roles,
	})
	if err != nil {
		return err
	}
	if err := s.Put(entry); err != nil {
		return err
	}
	return nil
}

// Retrieves the list of roles from the zeroaddress endpoint.
func (b *backend) getZeroAddressRoles(s logical.Storage) (*zeroAddressRoles, error) {
	entry, err := s.Get("config/zeroaddress")
	if err != nil {
		return nil, err
	}
	if entry == nil {
		return nil, nil
	}

	var result zeroAddressRoles
	if err := entry.DecodeJSON(&result); err != nil {
		return nil, err
	}

	return &result, nil
}

func (b *backend) removeZeroAddressRole(s logical.Storage, roleName string) error {
	zeroAddressEntry, err := b.getZeroAddressRoles(s)
	if err != nil {
		return err
	}
	if zeroAddressEntry == nil {
		return nil
	}

	err = zeroAddressEntry.Remove(roleName)
	if err != nil {
		return err
	}

	return b.putZeroAddressRoles(s, zeroAddressEntry.Roles)
}

func (r *zeroAddressRoles) Remove(roleName string) error {
	var index int
	for i, role := range r.Roles {
		if role == roleName {
			index = i
			break
		}
	}
	length := len(r.Roles)
	if index >= length || index < 0 {
		return fmt.Errorf("invalid index [%d]", index)
	}
	// If slice has zero or one item, remove the item by setting slice to nil.
	if length < 2 {
		r.Roles = nil
		return nil
	}

	// Last item to be deleted
	if length-1 == index {
		r.Roles = r.Roles[:length-1]
		return nil
	}

	// Delete the item by appending all items except the one at index
	r.Roles = append(r.Roles[:index], r.Roles[index+1:]...)
	return nil
}

const pathConfigZeroAddressSyn = `
Assign zero address as default CIDR block for select roles.
`

const pathConfigZeroAddressDesc = `
Administrator can choose to make a select few registered roles to accept any IP
address, overriding the CIDR blocks registered during creation of roles. This
doesn't mean that the credentials are created for any IP address. Clients who
have access to these roles are trusted to make valid requests. Access to these
roles should be controlled using Vault policies. It is recommended that all the
roles that are allowed to accept any IP address should have an explicit policy
of deny for unintended clients.

This is a root authenticated endpoint. If backend is mounted at 'ssh' then use
the endpoint 'ssh/config/zeroaddress' to provide the list of allowed roles.
After mounting the backend, use 'path-help' for additional information.
`
<|endoftext|>


<commit_before>package zmqtransport

import (
	"fmt"

	zmq "github.com/pebbe/zmq4"

	"git.apache.org/thrift.git/lib/go/thrift"
)

type WriteMessage struct {
	id   []byte
	data []byte
}

// TRouterServerTransport is a zeromq Router based TServerTransport implementation.
type TRouterServerTransport struct {
	transports map[string]TRouterTransport
	sock       *zmq.Socket
	endpoint   string
	cwrite     chan WriteMessage
	caccept    chan []byte
}

// NewTRouterServerTransport function instantiates a new TRouterServerTransport to specified endpoint
func NewTRouterServerTransport(endpoint string) TRouterServerTransport {
	fmt.Println("new TRouterServerTransport")
	sock, err := zmq.NewSocket(zmq.ROUTER)

	if err != nil {
		panic(err.Error())
	}

	cwrite := make(chan WriteMessage, 100)
	caccept := make(chan []byte, 100)
	go func() {
		for {
			msg := <-cwrite
			sock.SendMessage(msg.id, msg.data)
			//fmt.Println("send ", msg)
		}
	}()

	t := TRouterServerTransport{
		transports: make(map[string]TRouterTransport),
		sock:       sock,
		endpoint:   endpoint,
		caccept:    caccept,
		cwrite:     cwrite,
	}
	go func() {
		for {
			msg, err := sock.RecvMessageBytes(0)
			fmt.Println("number of peers ", len(t.transports))
			id := msg[0]
			var transport TRouterTransport
			if err == nil {
				buf := msg[1]

				if trans, ok := t.transports[string(id)]; !ok {
					cread := make(chan []byte, 100)
					transport = TRouterTransport{
						id:     id,
						cread:  cread,
						cwrite: t.cwrite,
						server: &t,
					}
					fmt.Println("create transport channel for", transport)

					t.transports[string(id)] = transport
					caccept <- id
				} else {
					transport = trans
				}
				if len(buf) == 0 {
					// received 0 bytes means close
					fmt.Println("received EOL")
					transport.Close()
				} else {
					transport.cread <- buf
				}
			}
		}
	}()
	return t
}

func (t TRouterServerTransport) Listen() error {
	t.sock.Bind(t.endpoint)
	fmt.Println("TRouterServerTransport.Listen ", t.endpoint)
	return nil
}

func (t TRouterServerTransport) Accept() (thrift.TTransport, error) {
	id := <-t.caccept
	fmt.Println("TRouterServerTransport.Accept ", id, t.transports[string(id)])
	return t.transports[string(id)], nil
}

func (t TRouterServerTransport) Close() error {
	fmt.Println("TRouterServerTransport.Close")

	transports := make([]TRouterTransport, 0)
	for _, trans := range t.transports {
		transports = append(transports, trans)
	}
	for _, trans := range transports {
		trans.Close()
	}
	return nil
}

func (t TRouterServerTransport) Interrupt() error {
	fmt.Println("TRouterServerTransport.Interrupt")
	return nil
}

// TRouterTransport is a TTransport implementation.
type TRouterTransport struct {
	id     []byte
	cread  chan []byte
	cwrite chan WriteMessage
	server *TRouterServerTransport
}

func (t TRouterTransport) Open() error {
	return nil
}

func (t TRouterTransport) IsOpen() bool {
	return true
}

func (t TRouterTransport) Close() error {
	fmt.Println("TRouterTransport.Close")
	t.cwrite <- WriteMessage{t.id, []byte{}} // send 0 bytes indicates close
	delete(t.server.transports, string(t.id))
	return nil
}

func (t TRouterTransport) Read(buf []byte) (l int, err error) {
	//fmt.Println("TRouterTransport.Read wait, len ", len(buf), buf)
	data := <-t.cread
	copy(buf, data)
	return len(data), nil
}

func (t TRouterTransport) Write(buf []byte) (int, error) {
	data := make([]byte, len(buf))
	copy(data, buf)
	msg := WriteMessage{
		id:   t.id,
		data: data,
	}

	t.cwrite <- msg
	//fmt.Println("write ", msg)
	return len(buf), nil
}

func (p TRouterTransport) Flush() error {
	return nil
}

func (p TRouterTransport) RemainingBytes() (num_bytes uint64) {
	//fmt.Println("RemainingBytes, hard code to 128")
	return uint64(4096)
}
<commit_msg>don't print number of peers<commit_after>package zmqtransport

import (
	"fmt"

	zmq "github.com/pebbe/zmq4"

	"git.apache.org/thrift.git/lib/go/thrift"
)

type WriteMessage struct {
	id   []byte
	data []byte
}

// TRouterServerTransport is a zeromq Router based TServerTransport implementation.
type TRouterServerTransport struct {
	transports map[string]TRouterTransport
	sock       *zmq.Socket
	endpoint   string
	cwrite     chan WriteMessage
	caccept    chan []byte
}

// NewTRouterServerTransport function instantiates a new TRouterServerTransport to specified endpoint
func NewTRouterServerTransport(endpoint string) TRouterServerTransport {
	fmt.Println("new TRouterServerTransport")
	sock, err := zmq.NewSocket(zmq.ROUTER)

	if err != nil {
		panic(err.Error())
	}

	cwrite := make(chan WriteMessage, 100)
	caccept := make(chan []byte, 100)
	go func() {
		for {
			msg := <-cwrite
			sock.SendMessage(msg.id, msg.data)
			//fmt.Println("send ", msg)
		}
	}()

	t := TRouterServerTransport{
		transports: make(map[string]TRouterTransport),
		sock:       sock,
		endpoint:   endpoint,
		caccept:    caccept,
		cwrite:     cwrite,
	}
	go func() {
		for {
			msg, err := sock.RecvMessageBytes(0)
			//fmt.Println("number of peers ", len(t.transports))
			id := msg[0]
			var transport TRouterTransport
			if err == nil {
				buf := msg[1]

				if trans, ok := t.transports[string(id)]; !ok {
					cread := make(chan []byte, 100)
					transport = TRouterTransport{
						id:     id,
						cread:  cread,
						cwrite: t.cwrite,
						server: &t,
					}
					fmt.Println("create transport channel for", transport)

					t.transports[string(id)] = transport
					caccept <- id
				} else {
					transport = trans
				}
				if len(buf) == 0 {
					// received 0 bytes means close
					fmt.Println("received EOL")
					transport.Close()
				} else {
					transport.cread <- buf
				}
			}
		}
	}()
	return t
}

func (t TRouterServerTransport) Listen() error {
	t.sock.Bind(t.endpoint)
	fmt.Println("TRouterServerTransport.Listen ", t.endpoint)
	return nil
}

func (t TRouterServerTransport) Accept() (thrift.TTransport, error) {
	id := <-t.caccept
	fmt.Println("TRouterServerTransport.Accept ", id, t.transports[string(id)])
	return t.transports[string(id)], nil
}

func (t TRouterServerTransport) Close() error {
	fmt.Println("TRouterServerTransport.Close")

	transports := make([]TRouterTransport, 0)
	for _, trans := range t.transports {
		transports = append(transports, trans)
	}
	for _, trans := range transports {
		trans.Close()
	}
	return nil
}

func (t TRouterServerTransport) Interrupt() error {
	fmt.Println("TRouterServerTransport.Interrupt")
	return nil
}

// TRouterTransport is a TTransport implementation.
type TRouterTransport struct {
	id     []byte
	cread  chan []byte
	cwrite chan WriteMessage
	server *TRouterServerTransport
}

func (t TRouterTransport) Open() error {
	return nil
}

func (t TRouterTransport) IsOpen() bool {
	return true
}

func (t TRouterTransport) Close() error {
	fmt.Println("TRouterTransport.Close")
	t.cwrite <- WriteMessage{t.id, []byte{}} // send 0 bytes indicates close
	delete(t.server.transports, string(t.id))
	return nil
}

func (t TRouterTransport) Read(buf []byte) (l int, err error) {
	//fmt.Println("TRouterTransport.Read wait, len ", len(buf), buf)
	data := <-t.cread
	copy(buf, data)
	return len(data), nil
}

func (t TRouterTransport) Write(buf []byte) (int, error) {
	data := make([]byte, len(buf))
	copy(data, buf)
	msg := WriteMessage{
		id:   t.id,
		data: data,
	}

	t.cwrite <- msg
	//fmt.Println("write ", msg)
	return len(buf), nil
}

func (p TRouterTransport) Flush() error {
	return nil
}

func (p TRouterTransport) RemainingBytes() (num_bytes uint64) {
	//fmt.Println("RemainingBytes, hard code to 128")
	return uint64(4096)
}
<|endoftext|>


<commit_before>package runtime

import (
	"net/http"
	"strings"

	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
)

const metadataHeaderPrefix = "Grpc-Metadata-"

/*
AnnotateContext adds context information such as metadata from the request.

If there are no metadata headers in the request, then the context returned
will be the same context.
*/
func AnnotateContext(ctx context.Context, req *http.Request) context.Context {
	var pairs []string
	for key, val := range req.Header {
		if strings.HasPrefix(key, metadataHeaderPrefix) {
			pairs = append(pairs, key[len(metadataHeaderPrefix):], val[0])
		}
	}

	if len(pairs) != 0 {
		ctx = metadata.NewContext(ctx, metadata.Pairs(pairs...))
	}
	return ctx
}
<commit_msg>Pass the Authorization header though, which should handle basic HTTP Auth and others such as OAuth which rely on tacking additional meaning into this.<commit_after>package runtime

import (
	"net/http"
	"strings"

	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
)

const metadataHeaderPrefix = "Grpc-Metadata-"

/*
AnnotateContext adds context information such as metadata from the request.

If there are no metadata headers in the request, then the context returned
will be the same context.
*/
func AnnotateContext(ctx context.Context, req *http.Request) context.Context {
	var pairs []string
	for key, val := range req.Header {
		if strings.HasPrefix(key, metadataHeaderPrefix) {
			pairs = append(pairs, key[len(metadataHeaderPrefix):], val[0])
		}
		if key == "Authorization" {
			pairs = append(pairs, key, val[0])
		}
	}

	if len(pairs) != 0 {
		ctx = metadata.NewContext(ctx, metadata.Pairs(pairs...))
	}
	return ctx
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/goauth2/oauth"
	"fmt"
	"github.com/digitalocean/godo"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"time"
)

func main() {
	regions := []string{"nyc3", "ams3", "fra1", "lon1", "sgp1", "sfo1"}

	pat := os.Getenv("DO_PAT")
	if pat == "" {
		fmt.Println("DO_PAT is required")
		return
	}

	t := &oauth.Transport{
		Token: &oauth.Token{AccessToken: pat},
	}
	client := godo.NewClient(t.Client())

	num, _ := strconv.ParseInt(os.Args[1], 10, 64)

	keys, _, _ := client.Keys.List(nil)

	var k godo.DropletCreateSSHKey
	for _, key := range keys {
		k = godo.DropletCreateSSHKey{
			ID:          key.ID,
			Fingerprint: key.Fingerprint,
		}
	}

	ks := []godo.DropletCreateSSHKey{k}

	var i int64
	for i = 0; i < num; i++ {
		resp, err := http.Get("http://meaningless-identifier.herokuapp.com/text")
		if err != nil {
			fmt.Printf("Something bad happened: %s\n\n", err)
			return
		}

		defer resp.Body.Close()

		byteArray, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Printf("Something bad happened: %s\n\n", err)
			return
		}

		name := string(byteArray)

		rand.Seed(time.Now().UnixNano())

		createRequest := &godo.DropletCreateRequest{
			Name:   fmt.Sprintf(name),
			Region: regions[rand.Intn(len(regions))],
			Size:   "512mb",
			Image: godo.DropletCreateImage{
				Slug: "ubuntu-14-04-x64",
			},
			SSHKeys: ks,
		}
		newDroplet, _, err := client.Droplets.Create(createRequest)

		if err != nil {
			fmt.Printf("Something bad happened: %s\n\n", err)
			return
		}

		fmt.Printf("%v\n", newDroplet)
	}
}
<commit_msg>Add some regions<commit_after>package main

import (
	"code.google.com/p/goauth2/oauth"
	"fmt"
	"github.com/digitalocean/godo"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"time"
)

func main() {
	regions := []string{"nyc3", "nyc2", "ams3", "ams2", "fra1", "lon1", "sgp1", "sfo1"}

	pat := os.Getenv("DO_PAT")
	if pat == "" {
		fmt.Println("DO_PAT is required")
		return
	}

	t := &oauth.Transport{
		Token: &oauth.Token{AccessToken: pat},
	}
	client := godo.NewClient(t.Client())

	num, _ := strconv.ParseInt(os.Args[1], 10, 64)

	keys, _, _ := client.Keys.List(nil)

	var k godo.DropletCreateSSHKey
	for _, key := range keys {
		k = godo.DropletCreateSSHKey{
			ID:          key.ID,
			Fingerprint: key.Fingerprint,
		}
	}

	ks := []godo.DropletCreateSSHKey{k}

	var i int64
	for i = 0; i < num; i++ {
		resp, err := http.Get("http://meaningless-identifier.herokuapp.com/text")
		if err != nil {
			fmt.Printf("Something bad happened: %s\n\n", err)
			return
		}

		defer resp.Body.Close()

		byteArray, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Printf("Something bad happened: %s\n\n", err)
			return
		}

		name := string(byteArray)

		rand.Seed(time.Now().UnixNano())

		createRequest := &godo.DropletCreateRequest{
			Name:   fmt.Sprintf(name),
			Region: regions[rand.Intn(len(regions))],
			Size:   "512mb",
			Image: godo.DropletCreateImage{
				Slug: "ubuntu-14-04-x64",
			},
			SSHKeys: ks,
		}
		newDroplet, _, err := client.Droplets.Create(createRequest)

		if err != nil {
			fmt.Printf("Something bad happened: %s\n\n", err)
			return
		}

		fmt.Printf("%v\n", newDroplet)
	}
}
<|endoftext|>


<commit_before>package smsclub

import (
	"bytes"
	"context"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"golang.org/x/text/encoding/charmap"
	"golang.org/x/text/transform"
)

type methodAPI int

const (
	mSend methodAPI = iota
	mStatus
	mBalance
)

var (
	epoint = "https://gate.smsclub.mobi/http"
	mapper = map[methodAPI]string{
		mSend:    "httpsendsms",
		mStatus:  "httpgetsmsstate",
		mBalance: "httpgetbalance",
	}
	makeURL = func(m methodAPI) string {
		return fmt.Sprintf("%s/%s.php", epoint, mapper[m])
	}
)

// SMSer is interface for https://smsclub.mobi/en/pages/show/api
type SMSer interface {
	// Balance returns values for balance and credit.
	Balance() (float64, float64, error)

	// LifeTime sets life time of SMS, which is specified in minutes.
	LifeTime(d time.Duration) error

	// Send sends SMS text message from "alphaName" to recipients.
	Send(from, text string, to ...string) ([]string, error)

	// Status gets list of SMS identifiers and returns statuses for ones.
	Status(ids ...string) ([]string, error)

	// Timeout sets timeout for calls Balance, Send and Status
	Timeout(d time.Duration) error
}

// New returns SMSer interface
func New(user, pass string) SMSer {
	return &client{
		user: user,
		pass: pass,
	}
}

type client struct {
	user    string
	pass    string
	ltime   int
	timeout time.Duration
}

func (c *client) makeForm(f url.Values) (url.Values, error) {
	if c.user == "" || c.pass == "" {
		return nil, fmt.Errorf("smsclub: username or password is empty")
	}
	if f == nil {
		f = url.Values{}
	}
	f.Set("username", c.user)
	f.Set("password", c.pass)
	if c.ltime > 0 {
		f.Set("lifetime", strconv.Itoa(c.ltime))
	}
	return f, nil
}

func closeBody(c io.Closer) {
	if c == nil {
		return
	}
	err := c.Close()
	if err != nil {
		panic(err)
	}
}

func (c *client) callAPI(m methodAPI, v url.Values) ([]string, error) {
	frm, err := c.makeForm(v)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", makeURL(m), strings.NewReader(frm.Encode()))
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if c.timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
		defer cancel()
		req = req.WithContext(ctx)
	}

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer closeBody(res.Body)

	if res.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("smsclub: server returns %d %s", res.StatusCode, res.Status)
	}

	bts, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	var out []string
	for _, s := range strings.Split(string(bts), "
") {
		if strings.Contains(s, "=") || strings.TrimSpace(s) == "" {
			continue
		}
		out = append(out, s)
	}

	if m != mBalance && !strings.Contains(string(bts), "=IDS") {
		return nil, fmt.Errorf("%s", strings.Join(out, "\n"))
	}

	if out == nil {
		return nil, io.EOF
	}

	return out, nil
}

func (c *client) Balance() (float64, float64, error) {
	res, err := c.callAPI(mBalance, nil)
	if err != nil {
		return 0.0, 0.0, err
	}

	var bal, cre float64
	if len(res) == 2 {
		bal, _ = strconv.ParseFloat(res[0], 64)
		cre, _ = strconv.ParseFloat(res[1], 64)
	}

	return bal, cre, nil
}

func (c *client) LifeTime(d time.Duration) error {
	if d < 0 {
		return fmt.Errorf("smsclub: invalid duration value %d", d)
	}
	c.ltime = int(d.Minutes())
	return nil
}

func (c *client) Send(from, text string, to ...string) ([]string, error) {
	toBase64 := func(s string) string {
		return base64.StdEncoding.EncodeToString([]byte(s))
	}
	toWin1251 := func(s string) string {
		b := new(bytes.Buffer)
		w := transform.NewWriter(b, charmap.Windows1251.NewEncoder())
		_, _ = w.Write([]byte(s))
		return b.String()
	}

	form := url.Values{
		"from": []string{from},
		"text": []string{toBase64(toWin1251(text))},
		"to":   []string{strings.Join(to, ";")},
	}

	return c.callAPI(mSend, form)
}

func (c *client) Status(ids ...string) ([]string, error) {
	form := url.Values{
		"smscid": []string{strings.Join(ids, ";")},
	}

	res, err := c.callAPI(mStatus, form)
	if err != nil {
		return nil, err
	}

	for i := range res {
		spl := strings.Split(res[i], ":")
		if len(spl) == 2 {
			res[i] = strings.TrimSpace(spl[1])
		}
	}

	return res, nil
}

func (c *client) String() string {
	return fmt.Sprintf("%s %s %d", c.user, c.pass, c.ltime)
}

func (c *client) Timeout(d time.Duration) error {
	if d < 0 {
		return fmt.Errorf("smsclub: invalid duration value %d", d)
	}
	c.timeout = d
	return nil
}
<commit_msg>fix end point<commit_after>package smsclub

import (
	"bytes"
	"context"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"golang.org/x/text/encoding/charmap"
	"golang.org/x/text/transform"
)

type methodAPI int

const (
	mSend methodAPI = iota
	mStatus
	mBalance
)

var (
	epoint = "https://gate.smsclub.mobi/token"
	mapper = map[methodAPI]string{
		mSend:    "httpsendsms",
		mStatus:  "httpgetsmsstate",
		mBalance: "httpgetbalance",
	}
	makeURL = func(m methodAPI) string {
		return fmt.Sprintf("%s/%s.php", epoint, mapper[m])
	}
)

// SMSer is interface for https://smsclub.mobi/en/pages/show/api
type SMSer interface {
	// Balance returns values for balance and credit.
	Balance() (float64, float64, error)

	// LifeTime sets life time of SMS, which is specified in minutes.
	LifeTime(d time.Duration) error

	// Send sends SMS text message from "alphaName" to recipients.
	Send(from, text string, to ...string) ([]string, error)

	// Status gets list of SMS identifiers and returns statuses for ones.
	Status(ids ...string) ([]string, error)

	// Timeout sets timeout for calls Balance, Send and Status
	Timeout(d time.Duration) error
}

// New returns SMSer interface
func New(user, pass string) SMSer {
	return &client{
		user: user,
		pass: pass,
	}
}

type client struct {
	user    string
	pass    string
	ltime   int
	timeout time.Duration
}

func (c *client) makeForm(f url.Values) (url.Values, error) {
	if c.user == "" || c.pass == "" {
		return nil, fmt.Errorf("smsclub: username or password is empty")
	}
	if f == nil {
		f = url.Values{}
	}
	f.Set("username", c.user)
	f.Set("password", c.pass)
	if c.ltime > 0 {
		f.Set("lifetime", strconv.Itoa(c.ltime))
	}
	return f, nil
}

func closeBody(c io.Closer) {
	if c == nil {
		return
	}
	err := c.Close()
	if err != nil {
		panic(err)
	}
}

func (c *client) callAPI(m methodAPI, v url.Values) ([]string, error) {
	frm, err := c.makeForm(v)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", makeURL(m), strings.NewReader(frm.Encode()))
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if c.timeout > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
		defer cancel()
		req = req.WithContext(ctx)
	}

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer closeBody(res.Body)

	if res.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("smsclub: server returns %d %s", res.StatusCode, res.Status)
	}

	bts, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	var out []string
	for _, s := range strings.Split(string(bts), "
") {
		if strings.Contains(s, "=") || strings.TrimSpace(s) == "" {
			continue
		}
		out = append(out, s)
	}

	if m != mBalance && !strings.Contains(string(bts), "=IDS") {
		return nil, fmt.Errorf("%s", strings.Join(out, "\n"))
	}

	if out == nil {
		return nil, io.EOF
	}

	return out, nil
}

func (c *client) Balance() (float64, float64, error) {
	res, err := c.callAPI(mBalance, nil)
	if err != nil {
		return 0.0, 0.0, err
	}

	var bal, cre float64
	if len(res) == 2 {
		bal, _ = strconv.ParseFloat(res[0], 64)
		cre, _ = strconv.ParseFloat(res[1], 64)
	}

	return bal, cre, nil
}

func (c *client) LifeTime(d time.Duration) error {
	if d < 0 {
		return fmt.Errorf("smsclub: invalid duration value %d", d)
	}
	c.ltime = int(d.Minutes())
	return nil
}

func (c *client) Send(from, text string, to ...string) ([]string, error) {
	toBase64 := func(s string) string {
		return base64.StdEncoding.EncodeToString([]byte(s))
	}
	toWin1251 := func(s string) string {
		b := new(bytes.Buffer)
		w := transform.NewWriter(b, charmap.Windows1251.NewEncoder())
		_, _ = w.Write([]byte(s))
		return b.String()
	}

	form := url.Values{
		"from": []string{from},
		"text": []string{toBase64(toWin1251(text))},
		"to":   []string{strings.Join(to, ";")},
	}

	return c.callAPI(mSend, form)
}

func (c *client) Status(ids ...string) ([]string, error) {
	form := url.Values{
		"smscid": []string{strings.Join(ids, ";")},
	}

	res, err := c.callAPI(mStatus, form)
	if err != nil {
		return nil, err
	}

	for i := range res {
		spl := strings.Split(res[i], ":")
		if len(spl) == 2 {
			res[i] = strings.TrimSpace(spl[1])
		}
	}

	return res, nil
}

func (c *client) String() string {
	return fmt.Sprintf("%s %s %d", c.user, c.pass, c.ltime)
}

func (c *client) Timeout(d time.Duration) error {
	if d < 0 {
		return fmt.Errorf("smsclub: invalid duration value %d", d)
	}
	c.timeout = d
	return nil
}
<|endoftext|>