<commit_before>// Package pinata is a utility to beat data out of interface{}, []interface{}
// and map[string]interface{}. It was origally designed for use with the
// encoding/json package but can be generally useful.
//
// Unlike other packages most methods do not return an error type. They become
// a no-op when the first error is found so the error can be checked after a
// series of operations instead of at each operation seperately. Because of
// this "late" error handling design special care is taken to return good
// errors so you can still find out where things went wrong.
//
// This API is not thread safe.
package pinata

import (
	"bytes"
	"fmt"
	"strings"
)

// Stick offers methods of hitting the Pinata and extracting its goodness.
type Stick interface {
	// Error returns the first error encountered or nil if all operations so far
	// were successful.
	Error() error

	// ClearError clears the error. If there is no error the method has no effect.
	ClearError()

	// PathString gets the string value at the given path within the Pinata. The
	// last element in the path must be a string, the rest must be a
	// map[string]interface{}. The input Pinata must hold a
	// map[string]interface{} as well.
	PathString(Pinata, ...string) string

	// String returns the Pinata as a string if it is one.
	String(Pinata) string

	// IndexString gets the string value at the given index within the Pinata.
	// The input Pinata must hold a []interface{}.
	IndexString(Pinata, int) string

	// PathPinata gets the Pinata value at the given path within the Pinata. All
	// elements in the path must be of type map[string]interface{}. The input
	// Pinata must hold a map[string]interface{} as well.
	PathPinata(Pinata, ...string) Pinata

	// IndexString gets the Pinata value at the given index within the Pinata.
	// The input Pinata must hold a []interface{}.
	IndexPinata(Pinata, int) Pinata
}

// Pinata holds the data.
type Pinata struct {
	context   *ErrorContext
	value     interface{}
	mapFunc   func() (map[string]interface{}, bool)
	sliceFunc func() ([]interface{}, bool)
}

// Value returns the raw Pinata value.
func (p Pinata) Value() interface{} {
	return p.value
}

// Map returns the Pinata value as a map if it is one (the bool indicates
// success).
func (p Pinata) Map() (map[string]interface{}, bool) {
	return p.mapFunc()
}

// Slice returns the Pinata value as a slice if it is one (the bool indicates
// success).
func (p Pinata) Slice() ([]interface{}, bool) {
	return p.sliceFunc()
}

// ErrorContext contains information about the circumstances of an error.
type ErrorContext struct {
	methodName string
	methodArgs func() []interface{}
	next       *ErrorContext
}

// MethodName returns the name of the method that caused the error.
func (ec ErrorContext) MethodName() string {
	return ec.methodName
}

// MethodArgs returns the input parameters of the method that caused the error.
func (ec ErrorContext) MethodArgs() []interface{} {
	return ec.methodArgs()
}

// Next gets additional context linked to this one.
func (ec ErrorContext) Next() (ErrorContext, bool) {
	if ec.next != nil {
		return *ec.next, true
	}
	return ErrorContext{}, false
}

// New is a starting point for a pinata celebration.
func New(contents interface{}) (Stick, Pinata) {
	return NewStick(), NewPinata(contents)
}

// NewStick returns a new Stick to hit a Pinata with.
func NewStick() Stick {
	return &stick{}
}

// NewPinata creates a new Pinata holding the specified value.
func NewPinata(contents interface{}) Pinata {
	return newPinataWithContext(contents, nil)
}

func noMap() (map[string]interface{}, bool) { return nil, false }
func noSlice() ([]interface{}, bool)        { return nil, false }

func newPinataWithContext(contents interface{}, context *ErrorContext) Pinata {
	switch t := contents.(type) {
	case map[string]interface{}:
		return Pinata{
			value:     t,
			sliceFunc: noSlice,
			mapFunc: func() (map[string]interface{}, bool) {
				return t, true
			},
			context: context,
		}
	case []interface{}:
		return Pinata{
			value: t,
			sliceFunc: func() ([]interface{}, bool) {
				return t, true
			},
			mapFunc: noMap,
			context: context,
		}
	default:
		return Pinata{
			value:     t,
			sliceFunc: noSlice,
			mapFunc:   noMap,
			context:   context,
		}
	}
}

// ErrorReason describes the reason for returning an Error.
type ErrorReason string

const (
	// ErrorReasonIncompatibleType indicates the contents of the Pinata is not compatible with the invoked method.
	ErrorReasonIncompatibleType ErrorReason = "incompatible type"
	// ErrorReasonNotFound indicates the input has not been found in the Pinata.
	ErrorReasonNotFound = "not found"
	// ErrorReasonInvalidInput indicates the input is not in the expected range or format.
	ErrorReasonInvalidInput = "invalid input"
)

// Error is set on the Pinata when something goes wrong.
type Error struct {
	reason  ErrorReason
	context *ErrorContext
	advice  string
}

// Reason indicates why the error occurred.
func (p Error) Reason() ErrorReason {
	return p.reason
}

// Context returns more information about the circumstances of the error.
func (p Error) Context() (ErrorContext, bool) {
	if p.context != nil {
		return *p.context, true
	}
	return ErrorContext{}, false
}

// Advice contains a human readable hint detailing how to remedy this error.
func (p Error) Advice() string {
	return p.advice
}

// Error returns a summary of the problem.
func (p Error) Error() string {
	var summaries []string
	current := p.context
	for current != nil {
		var methodArgs = current.MethodArgs()
		var summary string
		if len(methodArgs) > 0 {
			var buf bytes.Buffer
			_, _ = buf.WriteString(current.MethodName())
			_ = buf.WriteByte('(')
			for i := range methodArgs {
				_, _ = buf.WriteString("%#v")
				if i < len(methodArgs)-1 {
					_, _ = buf.WriteString(", ")
				}
			}
			_ = buf.WriteByte(')')
			summary = fmt.Sprintf(buf.String(), methodArgs...)
			summaries = append(summaries, summary)
		}
		current = current.next
	}
	return fmt.Sprintf("pinata: %s (%s): \n\t%v", p.Reason(), p.Advice(), strings.Join(summaries, " :: "))
}

type stick struct {
	err error
}

func (s *stick) ClearError() {
	s.err = nil
}

func (s *stick) Error() error {
	return s.err
}

// this method assumes s.err != nil
func (s *stick) stringUnsupported(errCtx *ErrorContext, methodName string, input func() []interface{}, advice string) string {
	s.err = &Error{
		context: &ErrorContext{
			methodName: methodName,
			methodArgs: input,
			next:       errCtx,
		},
		reason: ErrorReasonIncompatibleType,
		advice: advice,
	}
	return ""
}

// this method assumes s.err != nil
func (s *stick) indexUnsupported(errCtx *ErrorContext, methodName string, index int) {
	s.err = &Error{
		context: &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return []interface{}{index} },
			next:       errCtx,
		},
		reason: ErrorReasonIncompatibleType,
		advice: "call this method on a slice pinata",
	}
}

// this method assumes s.err != nil
func (s *stick) pathUnsupported(errCtx *ErrorContext, methodName string, path []string) {
	s.err = &Error{
		context: &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return toInterfaceSlice(path) },
			next:       errCtx,
		},
		reason: ErrorReasonIncompatibleType,
		advice: "call this method on a map pinata",
	}
}

// this method assumes s.err != nil
func (s *stick) internalString(p Pinata, methodName string, input func() []interface{}) string {
	if _, ok := p.Map(); ok {
		return s.stringUnsupported(p.context, methodName, input, "this is a map")
	}
	if _, ok := p.Slice(); ok {
		return s.stringUnsupported(p.context, methodName, input, "this is a slice")
	}
	if v, ok := p.Value().(string); ok {
		return v
	}
	return s.stringUnsupported(p.context, methodName, input, "this is not a string")
}

func (s *stick) String(p Pinata) string {
	if s.err != nil {
		return ""
	}
	return s.internalString(p, "String", func() []interface{} { return nil })
}

// this method assumes s.err != nil
func (s *stick) internalIndexPinata(p Pinata, methodName string, index int) Pinata {
	if slice, ok := p.Slice(); ok {
		if index < 0 || index >= len(slice) {
			s.err = &Error{
				context: &ErrorContext{
					methodName: methodName,
					methodArgs: func() []interface{} { return []interface{}{index} },
					next:       p.context,
				},
				reason: ErrorReasonInvalidInput,
				advice: fmt.Sprintf("specify an index from 0 to %d", len(slice)-1),
			}
			return Pinata{}
		}
		return newPinataWithContext(slice[index], &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return []interface{}{index} },
			next:       p.context,
		})
	}
	s.indexUnsupported(p.context, methodName, index)
	return Pinata{}
}

func (s *stick) IndexPinata(p Pinata, index int) Pinata {
	if s.err != nil {
		return Pinata{}
	}
	return s.internalIndexPinata(p, "IndexPinata", index)
}

func (s *stick) IndexString(p Pinata, index int) string {
	if s.err != nil {
		return ""
	}
	const methodName = "IndexString"
	pinata := s.internalIndexPinata(p, methodName, index)
	if s.err != nil {
		return ""
	}
	pinata.context = p.context
	return s.internalString(pinata, methodName, func() []interface{} { return []interface{}{index} })
}

// this method assumes s.err != nil
func (s *stick) internalPathPinata(p Pinata, methodName string, path ...string) Pinata {
	contents, ok := p.Map()

	if !ok {
		s.pathUnsupported(p.context, methodName, path)
		return Pinata{}
	}

	if len(path) == 0 {
		s.err = &Error{
			context: &ErrorContext{
				methodName: methodName,
				methodArgs: func() []interface{} { return toInterfaceSlice(path) },
				next:       p.context,
			},
			reason: ErrorReasonInvalidInput,
			advice: "specify a path",
		}
		return Pinata{}
	}

	for i := 0; i < len(path)-1; i++ {
		current := path[i]
		if v, ok := contents[current]; ok {
			if v, ok := v.(map[string]interface{}); ok {
				contents = v
			} else {
				s.err = &Error{
					context: &ErrorContext{
						methodName: methodName,
						methodArgs: func() []interface{} { return toInterfaceSlice(path) },
						next:       p.context,
					},
					reason: ErrorReasonIncompatibleType,
					advice: fmt.Sprintf(`"%s" does not hold a pinata`, strings.Join(path[:i+1], `", "`)),
				}
				return Pinata{}
			}
		} else {
			s.err = &Error{
				context: &ErrorContext{
					methodName: methodName,
					methodArgs: func() []interface{} { return toInterfaceSlice(path) },
					next:       p.context,
				},
				reason: ErrorReasonNotFound,
				advice: fmt.Sprintf(`"%s" does not exist`, strings.Join(path[:i+1], `", "`)),
			}
			return Pinata{}
		}
	}

	if v, ok := contents[path[len(path)-1]]; ok {
		return newPinataWithContext(v, &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return toInterfaceSlice(path) },
			next:       p.context,
		})
	}

	s.err = &Error{
		context: &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return toInterfaceSlice(path) },
			next:       p.context,
		},
		reason: ErrorReasonNotFound,
		advice: fmt.Sprintf(`"%s" does not exist`, strings.Join(path, `", "`)),
	}
	return Pinata{}
}

func (s *stick) PathPinata(p Pinata, path ...string) Pinata {
	if s.err != nil {
		return Pinata{}
	}
	return s.internalPathPinata(p, "PathPinata", path...)
}

func (s *stick) PathString(p Pinata, path ...string) string {
	if s.err != nil {
		return ""
	}
	const methodName = "PathString"
	pinata := s.internalPathPinata(p, methodName, path...)
	if s.err != nil {
		return ""
	}
	pinata.context = p.context
	return s.internalString(pinata, methodName, func() []interface{} { return toInterfaceSlice(path) })
}

func toInterfaceSlice(c []string) []interface{} {
	ifaces := make([]interface{}, len(c))
	for i := range c {
		ifaces[i] = c[i]
	}
	return ifaces
}
<commit_msg>reorder the members<commit_after>// Package pinata is a utility to beat data out of interface{}, []interface{}
// and map[string]interface{}. It was origally designed for use with the
// encoding/json package but can be generally useful.
//
// Unlike other packages most methods do not return an error type. They become
// a no-op when the first error is found so the error can be checked after a
// series of operations instead of at each operation seperately. Because of
// this "late" error handling design special care is taken to return good
// errors so you can still find out where things went wrong.
//
// This API is not thread safe.
package pinata

import (
	"bytes"
	"fmt"
	"strings"
)

// Stick offers methods of hitting the Pinata and extracting its goodness.
type Stick interface {
	// Error returns the first error encountered or nil if all operations so far
	// were successful.
	Error() error

	// ClearError clears the error. If there is no error the method has no effect.
	ClearError()

	// PathString gets the string value at the given path within the Pinata. The
	// last element in the path must be a string, the rest must be a
	// map[string]interface{}. The input Pinata must hold a
	// map[string]interface{} as well.
	PathString(Pinata, ...string) string

	// String returns the Pinata as a string if it is one.
	String(Pinata) string

	// IndexString gets the string value at the given index within the Pinata.
	// The input Pinata must hold a []interface{}.
	IndexString(Pinata, int) string

	// PathPinata gets the Pinata value at the given path within the Pinata. All
	// elements in the path must be of type map[string]interface{}. The input
	// Pinata must hold a map[string]interface{} as well.
	PathPinata(Pinata, ...string) Pinata

	// IndexString gets the Pinata value at the given index within the Pinata.
	// The input Pinata must hold a []interface{}.
	IndexPinata(Pinata, int) Pinata
}

type stick struct {
	err error
}

func (s *stick) ClearError() {
	s.err = nil
}

func (s *stick) Error() error {
	return s.err
}

// this method assumes s.err != nil
func (s *stick) stringUnsupported(errCtx *ErrorContext, methodName string, input func() []interface{}, advice string) string {
	s.err = &Error{
		context: &ErrorContext{
			methodName: methodName,
			methodArgs: input,
			next:       errCtx,
		},
		reason: ErrorReasonIncompatibleType,
		advice: advice,
	}
	return ""
}

// this method assumes s.err != nil
func (s *stick) indexUnsupported(errCtx *ErrorContext, methodName string, index int) {
	s.err = &Error{
		context: &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return []interface{}{index} },
			next:       errCtx,
		},
		reason: ErrorReasonIncompatibleType,
		advice: "call this method on a slice pinata",
	}
}

// this method assumes s.err != nil
func (s *stick) pathUnsupported(errCtx *ErrorContext, methodName string, path []string) {
	s.err = &Error{
		context: &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return toInterfaceSlice(path) },
			next:       errCtx,
		},
		reason: ErrorReasonIncompatibleType,
		advice: "call this method on a map pinata",
	}
}

// this method assumes s.err != nil
func (s *stick) internalString(p Pinata, methodName string, input func() []interface{}) string {
	if _, ok := p.Map(); ok {
		return s.stringUnsupported(p.context, methodName, input, "this is a map")
	}
	if _, ok := p.Slice(); ok {
		return s.stringUnsupported(p.context, methodName, input, "this is a slice")
	}
	if v, ok := p.Value().(string); ok {
		return v
	}
	return s.stringUnsupported(p.context, methodName, input, "this is not a string")
}

func (s *stick) String(p Pinata) string {
	if s.err != nil {
		return ""
	}
	return s.internalString(p, "String", func() []interface{} { return nil })
}

// this method assumes s.err != nil
func (s *stick) internalIndexPinata(p Pinata, methodName string, index int) Pinata {
	if slice, ok := p.Slice(); ok {
		if index < 0 || index >= len(slice) {
			s.err = &Error{
				context: &ErrorContext{
					methodName: methodName,
					methodArgs: func() []interface{} { return []interface{}{index} },
					next:       p.context,
				},
				reason: ErrorReasonInvalidInput,
				advice: fmt.Sprintf("specify an index from 0 to %d", len(slice)-1),
			}
			return Pinata{}
		}
		return newPinataWithContext(slice[index], &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return []interface{}{index} },
			next:       p.context,
		})
	}
	s.indexUnsupported(p.context, methodName, index)
	return Pinata{}
}

func (s *stick) IndexPinata(p Pinata, index int) Pinata {
	if s.err != nil {
		return Pinata{}
	}
	return s.internalIndexPinata(p, "IndexPinata", index)
}

func (s *stick) IndexString(p Pinata, index int) string {
	if s.err != nil {
		return ""
	}
	const methodName = "IndexString"
	pinata := s.internalIndexPinata(p, methodName, index)
	if s.err != nil {
		return ""
	}
	pinata.context = p.context
	return s.internalString(pinata, methodName, func() []interface{} { return []interface{}{index} })
}

// this method assumes s.err != nil
func (s *stick) internalPathPinata(p Pinata, methodName string, path ...string) Pinata {
	contents, ok := p.Map()

	if !ok {
		s.pathUnsupported(p.context, methodName, path)
		return Pinata{}
	}

	if len(path) == 0 {
		s.err = &Error{
			context: &ErrorContext{
				methodName: methodName,
				methodArgs: func() []interface{} { return toInterfaceSlice(path) },
				next:       p.context,
			},
			reason: ErrorReasonInvalidInput,
			advice: "specify a path",
		}
		return Pinata{}
	}

	for i := 0; i < len(path)-1; i++ {
		current := path[i]
		if v, ok := contents[current]; ok {
			if v, ok := v.(map[string]interface{}); ok {
				contents = v
			} else {
				s.err = &Error{
					context: &ErrorContext{
						methodName: methodName,
						methodArgs: func() []interface{} { return toInterfaceSlice(path) },
						next:       p.context,
					},
					reason: ErrorReasonIncompatibleType,
					advice: fmt.Sprintf(`"%s" does not hold a pinata`, strings.Join(path[:i+1], `", "`)),
				}
				return Pinata{}
			}
		} else {
			s.err = &Error{
				context: &ErrorContext{
					methodName: methodName,
					methodArgs: func() []interface{} { return toInterfaceSlice(path) },
					next:       p.context,
				},
				reason: ErrorReasonNotFound,
				advice: fmt.Sprintf(`"%s" does not exist`, strings.Join(path[:i+1], `", "`)),
			}
			return Pinata{}
		}
	}

	if v, ok := contents[path[len(path)-1]]; ok {
		return newPinataWithContext(v, &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return toInterfaceSlice(path) },
			next:       p.context,
		})
	}

	s.err = &Error{
		context: &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return toInterfaceSlice(path) },
			next:       p.context,
		},
		reason: ErrorReasonNotFound,
		advice: fmt.Sprintf(`"%s" does not exist`, strings.Join(path, `", "`)),
	}
	return Pinata{}
}

func (s *stick) PathPinata(p Pinata, path ...string) Pinata {
	if s.err != nil {
		return Pinata{}
	}
	return s.internalPathPinata(p, "PathPinata", path...)
}

func (s *stick) PathString(p Pinata, path ...string) string {
	if s.err != nil {
		return ""
	}
	const methodName = "PathString"
	pinata := s.internalPathPinata(p, methodName, path...)
	if s.err != nil {
		return ""
	}
	pinata.context = p.context
	return s.internalString(pinata, methodName, func() []interface{} { return toInterfaceSlice(path) })
}

// Pinata holds the data.
type Pinata struct {
	context   *ErrorContext
	value     interface{}
	mapFunc   func() (map[string]interface{}, bool)
	sliceFunc func() ([]interface{}, bool)
}

// Value returns the raw Pinata value.
func (p Pinata) Value() interface{} {
	return p.value
}

// Map returns the Pinata value as a map if it is one (the bool indicates
// success).
func (p Pinata) Map() (map[string]interface{}, bool) {
	return p.mapFunc()
}

// Slice returns the Pinata value as a slice if it is one (the bool indicates
// success).
func (p Pinata) Slice() ([]interface{}, bool) {
	return p.sliceFunc()
}

// New is a starting point for a pinata celebration.
func New(contents interface{}) (Stick, Pinata) {
	return NewStick(), NewPinata(contents)
}

// NewStick returns a new Stick to hit a Pinata with.
func NewStick() Stick {
	return &stick{}
}

// NewPinata creates a new Pinata holding the specified value.
func NewPinata(contents interface{}) Pinata {
	return newPinataWithContext(contents, nil)
}

func noMap() (map[string]interface{}, bool) { return nil, false }
func noSlice() ([]interface{}, bool)        { return nil, false }

func newPinataWithContext(contents interface{}, context *ErrorContext) Pinata {
	switch t := contents.(type) {
	case map[string]interface{}:
		return Pinata{
			value:     t,
			sliceFunc: noSlice,
			mapFunc: func() (map[string]interface{}, bool) {
				return t, true
			},
			context: context,
		}
	case []interface{}:
		return Pinata{
			value: t,
			sliceFunc: func() ([]interface{}, bool) {
				return t, true
			},
			mapFunc: noMap,
			context: context,
		}
	default:
		return Pinata{
			value:     t,
			sliceFunc: noSlice,
			mapFunc:   noMap,
			context:   context,
		}
	}
}

// ErrorReason describes the reason for returning an Error.
type ErrorReason string

const (
	// ErrorReasonIncompatibleType indicates the contents of the Pinata is not compatible with the invoked method.
	ErrorReasonIncompatibleType ErrorReason = "incompatible type"
	// ErrorReasonNotFound indicates the input has not been found in the Pinata.
	ErrorReasonNotFound = "not found"
	// ErrorReasonInvalidInput indicates the input is not in the expected range or format.
	ErrorReasonInvalidInput = "invalid input"
)

// ErrorContext contains information about the circumstances of an error.
type ErrorContext struct {
	methodName string
	methodArgs func() []interface{}
	next       *ErrorContext
}

// MethodName returns the name of the method that caused the error.
func (ec ErrorContext) MethodName() string {
	return ec.methodName
}

// MethodArgs returns the input parameters of the method that caused the error.
func (ec ErrorContext) MethodArgs() []interface{} {
	return ec.methodArgs()
}

// Next gets additional context linked to this one.
func (ec ErrorContext) Next() (ErrorContext, bool) {
	if ec.next != nil {
		return *ec.next, true
	}
	return ErrorContext{}, false
}

// Error is set on the Pinata when something goes wrong.
type Error struct {
	reason  ErrorReason
	context *ErrorContext
	advice  string
}

// Reason indicates why the error occurred.
func (p Error) Reason() ErrorReason {
	return p.reason
}

// Context returns more information about the circumstances of the error.
func (p Error) Context() (ErrorContext, bool) {
	if p.context != nil {
		return *p.context, true
	}
	return ErrorContext{}, false
}

// Advice contains a human readable hint detailing how to remedy this error.
func (p Error) Advice() string {
	return p.advice
}

// Error returns a summary of the problem.
func (p Error) Error() string {
	var summaries []string
	current := p.context
	for current != nil {
		var methodArgs = current.MethodArgs()
		var summary string
		if len(methodArgs) > 0 {
			var buf bytes.Buffer
			_, _ = buf.WriteString(current.MethodName())
			_ = buf.WriteByte('(')
			for i := range methodArgs {
				_, _ = buf.WriteString("%#v")
				if i < len(methodArgs)-1 {
					_, _ = buf.WriteString(", ")
				}
			}
			_ = buf.WriteByte(')')
			summary = fmt.Sprintf(buf.String(), methodArgs...)
			summaries = append(summaries, summary)
		}
		current = current.next
	}
	return fmt.Sprintf("pinata: %s (%s): \n\t%v", p.Reason(), p.Advice(), strings.Join(summaries, " :: "))
}

func toInterfaceSlice(c []string) []interface{} {
	ifaces := make([]interface{}, len(c))
	for i := range c {
		ifaces[i] = c[i]
	}
	return ifaces
}
<|endoftext|>


<commit_before>package pinger

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"log"
	"math/rand"
	"net"
	"sync"
	"time"

	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
)

const ProtocolICMP = 1

type PingStats struct {
	Latency  []time.Duration
	Sent     int
	Received int
	SentTime map[int]time.Time
}

type EchoRequest struct {
	Peer     string
	Count    int
	Deadline time.Time
	Sent     time.Time
	Id       int
	Stats    *PingStats
	Done     chan *PingStats
	Recv     chan *EchoResponse

	m      sync.RWMutex
	pinger *Pinger
}

type EchoResponse struct {
	Peer     string
	Id       int
	Seq      int
	Received time.Time
	Sent     time.Time
}

func (e *EchoResponse) String() string {
	return fmt.Sprintf("Peer %s, Id: %d, Seq: %d, Sent: %s, Received: %s", e.Peer, e.Id, e.Seq, e.Sent, e.Received)
}

func (e *EchoRequest) Listen() {
	seqRecv := make(map[int]bool)

	timer := time.NewTimer(e.Deadline.Sub(time.Now()))
WAIT:
	for {
		select {
		case <-timer.C:
			// deadline reached.
			log.Printf("go-pinger: deadline reached waiting for repsonse. Peer: %s, Id: %d", e.Peer, e.Id)
			break WAIT
		case resp := <-e.Recv:
			e.m.Lock()
			rtt := resp.Received.Sub(resp.Sent)
			e.Stats.Received++
			e.Stats.Latency = append(e.Stats.Latency, rtt)
			e.m.Unlock()

			seqRecv[resp.Seq] = true
			// we have recieved responses for all pings sent.
			if e.Stats.Received >= e.Stats.Sent {
				break WAIT
			}
		}
	}
	timer.Stop()
	for i := 0; i < e.Count; i++ {
		if _, ok := seqRecv[i]; !ok {
			key := packetKey(e.Peer, e.Id, i)
			e.pinger.DeleteKey(key)
		}
	}

	e.Done <- e.Stats
}

func (e *EchoRequest) Send() {
	data := make([]byte, 9)
	binary.PutVarint(data, time.Now().UnixNano())
	for i := 0; i < e.Count; i++ {
		pkt := icmp.Message{
			Type: ipv4.ICMPTypeEcho,
			Code: 0,
			Body: &icmp.Echo{
				ID:   e.Id,
				Seq:  i,
				Data: data,
			},
		}

		wb, err := pkt.Marshal(nil)
		if err != nil {
			if e.pinger.Debug {
				log.Printf("failed to marshal ICMP Echo packet. %s", err)
			}
			continue
		}
		e.Stats.Sent++
		e.pinger.WritePkt(wb, e.Peer)
	}
}

type Pinger struct {
	queue      map[string]chan *EchoResponse
	m          sync.RWMutex
	running    bool
	conn       *icmp.PacketConn
	Debug      bool
	packetChan chan EchoResponse
	Counter    int
}

func NewPinger() *Pinger {
	rand.Seed(time.Now().UnixNano())
	return &Pinger{queue: make(map[string]chan *EchoResponse), Counter: rand.Intn(0xffff)}
}

func packetKey(addr string, id, seq int) string {
	return fmt.Sprintf("%s-%d-%d", addr, id, seq)
}

func (p *Pinger) Ping(address string, count int, deadline time.Time) (<-chan *PingStats, error) {
	// ensuire the IP address is valid.
	ipAddr := net.ParseIP(address)
	if ipAddr == nil {
		return nil, fmt.Errorf("Failed to parse IP address")
	}

	p.m.Lock()
	defer p.m.Unlock()

	p.Counter++

	req := &EchoRequest{
		Peer:     address,
		Count:    count,
		Deadline: deadline,
		Id:       p.Counter,

		Done:  make(chan *PingStats),
		Recv:  make(chan *EchoResponse, count),
		Stats: &PingStats{Latency: make([]time.Duration, 0), SentTime: make(map[int]time.Time)},

		pinger: p,
	}

	for i := 0; i < count; i++ {
		key := packetKey(req.Peer, req.Id, i)
		p.queue[key] = req.Recv
	}

	if !p.running {
		p.start()
	}

	go req.Listen()
	go req.Send()

	return req.Done, nil
}

func (p *Pinger) start() {
	c, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
	if err != nil {
		panic(err)
	}
	if p.Debug {
		log.Printf("Listening on socket for ip4:icmp packets.")
	}
	p.conn = c
	p.running = true
	p.packetChan = make(chan EchoResponse, 10000)
	go p.listenIpv4()
	go p.processPkt()
}

func (p *Pinger) Stop() {
	p.running = false
	p.conn.Close()
	if p.Debug {
		log.Printf("Socket closed.")
	}
	p.conn = nil
	close(p.packetChan)
}

func (p *Pinger) listenIpv4() {
	rb := make([]byte, 1500)
	pkt := EchoResponse{}
	var readErr error
	var data []byte
	for {
		n, peer, err := p.conn.ReadFrom(rb)
		pktTime := time.Now()
		if err != nil {
			readErr = err
			break
		}
		rm, err := icmp.ParseMessage(ProtocolICMP, rb[:n])
		if err != nil {
			fmt.Println(err.Error())
			continue
		}
		if rm.Type == ipv4.ICMPTypeEchoReply {
			data = rm.Body.(*icmp.Echo).Data
			if len(data) != 9 {
				log.Printf("go-pinger: invalid data payload. Expected 8bytes got %d", len(data))
				continue
			}
			sentTime, err := binary.ReadVarint(bytes.NewReader(data))
			if err != nil {
				log.Printf("go-pinger: failed to marshal data to int64 number. %s", err.Error())
				continue
			}
			pkt = EchoResponse{
				Peer:     peer.String(),
				Seq:      rm.Body.(*icmp.Echo).Seq,
				Id:       rm.Body.(*icmp.Echo).ID,
				Received: pktTime,
				Sent:     time.Unix(0, sentTime),
			}
			if p.Debug || peer.String() == "147.75.194.137" {
				log.Printf("go-pinger: recieved %s\n", pkt.String())
			}
			select {
			case p.packetChan <- pkt:
			default:
				log.Printf("go-pinger: droped echo response due to blocked packetChan. %s\n", pkt.String())
			}

		}
	}
	if p.Debug {
		log.Printf("listen loop ended.")
	}
	p.m.Lock()
	if p.running {
		log.Println(readErr.Error())
		p.Stop()
		p.start()
	}
	p.m.Unlock()
}

func (p *Pinger) processPkt() {
	for pkt := range p.packetChan {
		key := packetKey(pkt.Peer, pkt.Id, pkt.Seq)
		p.m.RLock()
		req, ok := p.queue[key]
		delete(p.queue, key)
		p.m.RUnlock()
		if ok {
			if p.Debug {
				log.Printf("reply packet matches request packet. %s\n", pkt.String())
			}
			select {
			case req <- &pkt:
			default:
				log.Printf("go-pinger: droped echo response due to blocked response chan. %s", pkt.String())
			}
		} else {
			log.Printf("go-pinger: unexpected echo response. %s\n", pkt.String())
		}
	}
}

func (p *Pinger) DeleteKey(key string) {
	p.m.Lock()
	delete(p.queue, key)
	p.m.Unlock()
}

func (p *Pinger) WritePkt(b []byte, dst string) {
	if _, err := p.conn.WriteTo(b, &net.IPAddr{IP: net.ParseIP(dst)}); err != nil {
		if p.Debug {
			fmt.Printf("Failed to write packet to socket. %s", err)
		}
	}
}
<commit_msg>packet IDs must be between 0-65535<commit_after>package pinger

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"log"
	"math/rand"
	"net"
	"sync"
	"time"

	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
)

const ProtocolICMP = 1

type PingStats struct {
	Latency  []time.Duration
	Sent     int
	Received int
	SentTime map[int]time.Time
}

type EchoRequest struct {
	Peer     string
	Count    int
	Deadline time.Time
	Sent     time.Time
	Id       int
	Stats    *PingStats
	Done     chan *PingStats
	Recv     chan *EchoResponse

	m      sync.RWMutex
	pinger *Pinger
}

type EchoResponse struct {
	Peer     string
	Id       int
	Seq      int
	Received time.Time
	Sent     time.Time
}

func (e *EchoResponse) String() string {
	return fmt.Sprintf("Peer %s, Id: %d, Seq: %d, Sent: %s, Received: %s", e.Peer, e.Id, e.Seq, e.Sent, e.Received)
}

func (e *EchoRequest) Listen() {
	seqRecv := make(map[int]bool)

	timer := time.NewTimer(e.Deadline.Sub(time.Now()))
WAIT:
	for {
		select {
		case <-timer.C:
			// deadline reached.
			log.Printf("go-pinger: deadline reached waiting for repsonse. Peer: %s, Id: %d", e.Peer, e.Id)
			break WAIT
		case resp := <-e.Recv:
			e.m.Lock()
			rtt := resp.Received.Sub(resp.Sent)
			e.Stats.Received++
			e.Stats.Latency = append(e.Stats.Latency, rtt)
			e.m.Unlock()

			seqRecv[resp.Seq] = true
			// we have recieved responses for all pings sent.
			if e.Stats.Received >= e.Stats.Sent {
				break WAIT
			}
		}
	}
	timer.Stop()
	for i := 0; i < e.Count; i++ {
		if _, ok := seqRecv[i]; !ok {
			key := packetKey(e.Peer, e.Id, i)
			e.pinger.DeleteKey(key)
		}
	}

	e.Done <- e.Stats
}

func (e *EchoRequest) Send() {
	sentTime := time.Now()
	data := make([]byte, 9)
	binary.PutVarint(data, sentTime.UnixNano())
	for i := 0; i < e.Count; i++ {
		pkt := icmp.Message{
			Type: ipv4.ICMPTypeEcho,
			Code: 0,
			Body: &icmp.Echo{
				ID:   e.Id,
				Seq:  i,
				Data: data,
			},
		}

		wb, err := pkt.Marshal(nil)
		if err != nil {
			if e.pinger.Debug {
				log.Printf("failed to marshal ICMP Echo packet. %s", err)
			}
			continue
		}

		e.Stats.Sent++
		e.pinger.WritePkt(wb, e.Peer)
		if e.pinger.Debug || e.Peer == "147.75.194.137" {
			log.Printf("go-pinger: sent pkt. Peer %s, Id: %d, Seq: %d, Sent: %s", e.Peer, e.Id, i, sentTime)
		}
	}
}

type Pinger struct {
	queue      map[string]chan *EchoResponse
	m          sync.RWMutex
	running    bool
	conn       *icmp.PacketConn
	Debug      bool
	packetChan chan EchoResponse
	Counter    int
}

func NewPinger() *Pinger {
	rand.Seed(time.Now().UnixNano())
	return &Pinger{queue: make(map[string]chan *EchoResponse), Counter: rand.Intn(0xffff)}
}

func packetKey(addr string, id, seq int) string {
	return fmt.Sprintf("%s-%d-%d", addr, id, seq)
}

func (p *Pinger) Ping(address string, count int, deadline time.Time) (<-chan *PingStats, error) {
	// ensuire the IP address is valid.
	ipAddr := net.ParseIP(address)
	if ipAddr == nil {
		return nil, fmt.Errorf("Failed to parse IP address")
	}

	p.m.Lock()
	defer p.m.Unlock()

	p.Counter++
	if p.Counter > 65535 {
		p.Counter = 0
	}

	req := &EchoRequest{
		Peer:     address,
		Count:    count,
		Deadline: deadline,
		Id:       p.Counter,

		Done:  make(chan *PingStats),
		Recv:  make(chan *EchoResponse, count),
		Stats: &PingStats{Latency: make([]time.Duration, 0), SentTime: make(map[int]time.Time)},

		pinger: p,
	}

	for i := 0; i < count; i++ {
		key := packetKey(req.Peer, req.Id, i)
		p.queue[key] = req.Recv
	}

	if !p.running {
		p.start()
	}

	go req.Listen()
	go req.Send()

	return req.Done, nil
}

func (p *Pinger) start() {
	c, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
	if err != nil {
		panic(err)
	}
	if p.Debug {
		log.Printf("Listening on socket for ip4:icmp packets.")
	}
	p.conn = c
	p.running = true
	p.packetChan = make(chan EchoResponse, 10000)
	go p.listenIpv4()
	go p.processPkt()
}

func (p *Pinger) Stop() {
	p.running = false
	p.conn.Close()
	if p.Debug {
		log.Printf("Socket closed.")
	}
	p.conn = nil
	close(p.packetChan)
}

func (p *Pinger) listenIpv4() {
	rb := make([]byte, 1500)
	pkt := EchoResponse{}
	var readErr error
	var data []byte
	for {
		n, peer, err := p.conn.ReadFrom(rb)
		pktTime := time.Now()
		if err != nil {
			readErr = err
			break
		}
		rm, err := icmp.ParseMessage(ProtocolICMP, rb[:n])
		if err != nil {
			fmt.Println(err.Error())
			continue
		}
		if rm.Type == ipv4.ICMPTypeEchoReply {
			data = rm.Body.(*icmp.Echo).Data
			if len(data) != 9 {
				log.Printf("go-pinger: invalid data payload. Expected 8bytes got %d", len(data))
				continue
			}
			sentTime, err := binary.ReadVarint(bytes.NewReader(data))
			if err != nil {
				log.Printf("go-pinger: failed to marshal data to int64 number. %s", err.Error())
				continue
			}
			pkt = EchoResponse{
				Peer:     peer.String(),
				Seq:      rm.Body.(*icmp.Echo).Seq,
				Id:       rm.Body.(*icmp.Echo).ID,
				Received: pktTime,
				Sent:     time.Unix(0, sentTime),
			}
			if p.Debug || peer.String() == "147.75.194.137" {
				log.Printf("go-pinger: recieved %s\n", pkt.String())
			}
			select {
			case p.packetChan <- pkt:
			default:
				log.Printf("go-pinger: droped echo response due to blocked packetChan. %s\n", pkt.String())
			}

		}
	}
	if p.Debug {
		log.Printf("listen loop ended.")
	}
	p.m.Lock()
	if p.running {
		log.Println(readErr.Error())
		p.Stop()
		p.start()
	}
	p.m.Unlock()
}

func (p *Pinger) processPkt() {
	for pkt := range p.packetChan {
		key := packetKey(pkt.Peer, pkt.Id, pkt.Seq)
		p.m.RLock()
		req, ok := p.queue[key]
		delete(p.queue, key)
		p.m.RUnlock()
		if ok {
			if p.Debug {
				log.Printf("reply packet matches request packet. %s\n", pkt.String())
			}
			select {
			case req <- &pkt:
			default:
				log.Printf("go-pinger: droped echo response due to blocked response chan. %s", pkt.String())
			}
		} else {
			log.Printf("go-pinger: unexpected echo response. %s\n", pkt.String())
		}
	}
}

func (p *Pinger) DeleteKey(key string) {
	p.m.Lock()
	delete(p.queue, key)
	p.m.Unlock()
}

func (p *Pinger) WritePkt(b []byte, dst string) {
	if _, err := p.conn.WriteTo(b, &net.IPAddr{IP: net.ParseIP(dst)}); err != nil {
		if p.Debug {
			fmt.Printf("Failed to write packet to socket. %s", err)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package agent implements a service that runs on a serviced node. It is
// responsible for ensuring that a particular node is running the correct services
// and reporting the state and health of those services back to the master
// serviced.

package node

import (
	"github.com/zenoss/glog"

	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"os/user"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
)

// validOwnerSpec returns true if the owner is specified in owner:group format and the
// identifiers are valid POSIX.1-2008 username and group strings, respectively.
func validOwnerSpec(owner string) bool {
	var pattern = regexp.MustCompile(`^[a-zA-Z]+[a-zA-Z0-9.-]*:[a-zA-Z]+[a-zA-Z0-9.-]*$`)
	return pattern.MatchString(owner)
}

// GetInterfaceIPAddress attempts to find the IP address based on interface name
func GetInterfaceIPAddress(_interface string) (string, error) {
	output, err := exec.Command("/sbin/ip", "-4", "-o", "addr").Output()
	if err != nil {
		return "", err
	}

	for _, line := range strings.Split(string(output), "\n") {
		fields := strings.Fields(line)
		if len(fields) < 4 {
			continue
		}

		if strings.HasPrefix(fields[1], _interface) {
			return strings.Split(fields[3], "/")[0], nil
		}
	}

	return "", fmt.Errorf("Unable to find ip for interface: %s", _interface)
}

// getIPAddrFromOutGoingConnection get the IP bound to the interface which
// handles the default route traffic.
func getIPAddrFromOutGoingConnection() (ip string, err error) {
	addr, err := net.ResolveUDPAddr("udp4", "8.8.8.8:53")
	if err != nil {
		return "", err
	}

	conn, err := net.DialUDP("udp4", nil, addr)
	if err != nil {
		return "", err
	}

	localAddr := conn.LocalAddr()
	parts := strings.Split(localAddr.String(), ":")
	return parts[0], nil
}

// ExecPath returns the path to the currently running executable.
func ExecPath() (string, string, error) {
	path, err := os.Readlink("/proc/self/exe")
	if err != nil {
		return "", "", err
	}
	return filepath.Dir(path), filepath.Base(path), nil
}

// DockerVersion contains the tuples that describe the version of docker.
type DockerVersion struct {
	Client []int
	Server []int
}

// equals compares two DockerVersion structs and returns true if they are equal.
func (a *DockerVersion) equals(b *DockerVersion) bool {
	if len(a.Client) != len(b.Client) {
		return false
	}
	for i, aI := range a.Client {
		if aI != b.Client[i] {
			return false
		}
	}
	if len(a.Server) != len(b.Server) {
		return false
	}
	for i, aI := range a.Server {
		if aI != b.Server[i] {
			return false
		}
	}
	return true
}

// GetDockerVersion returns docker version number.
func GetDockerVersion() (DockerVersion, error) {
	cmd := exec.Command("docker", "version")
	output, err := cmd.Output()
	if err != nil {
		return DockerVersion{}, err
	}
	return parseDockerVersion(string(output))
}

// parseDockerVersion parses the output of the 'docker version' commmand and
// returns a DockerVersion object.
func parseDockerVersion(output string) (version DockerVersion, err error) {

	for _, line := range strings.Split(output, "\n") {
		parts := strings.SplitN(line, ":", 2)
		if len(parts) < 2 {
			continue
		}
		if strings.HasPrefix(parts[0], "Client version") {
			a := strings.SplitN(strings.TrimSpace(parts[1]), "-", 2)
			b := strings.Split(a[0], ".")
			version.Client = make([]int, len(b))
			for i, v := range b {
				x, err := strconv.Atoi(v)
				if err != nil {
					return version, err
				}
				version.Client[i] = x
			}
		}
		if strings.HasPrefix(parts[0], "Server version") {
			a := strings.SplitN(strings.TrimSpace(parts[1]), "-", 2)
			b := strings.Split(a[0], ".")
			version.Server = make([]int, len(b))
			for i, v := range b {
				x, err := strconv.Atoi(v)
				if err != nil {
					return version, err
				}
				version.Server[i] = x
			}
		}
	}
	if len(version.Client) == 0 {
		return version, fmt.Errorf("no client version found")
	}
	if len(version.Server) == 0 {
		return version, fmt.Errorf("no server version found")
	}
	return version, nil
}

// CreateDirectory creates a directory using the given username as the owner and the
// given perm as the directory permission.
func CreateDirectory(path, username string, perm os.FileMode) error {
	user, err := user.Lookup(username)
	if err == nil {
		err = os.MkdirAll(path, perm)
		if err == nil || err == os.ErrExist {
			uid, _ := strconv.Atoi(user.Uid)
			gid, _ := strconv.Atoi(user.Gid)
			err = os.Chown(path, uid, gid)
		}
	}
	return err
}

// singleJoiningSlash joins a and b ensuring there is only a single /
// character between them.
func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

// NewReverseProxy differs from httputil.NewSingleHostReverseProxy in that it rewrites
// the path so that it does /not/ include the incoming path. e.g. request for
// "/mysvc/thing" when proxy is served from "/mysvc" means target is
// targeturl.Path + "/thing"; vs. httputil.NewSingleHostReverseProxy, in which
// it would be targeturl.Path + "/mysvc/thing".
func NewReverseProxy(path string, targeturl *url.URL) *httputil.ReverseProxy {
	targetQuery := targeturl.RawQuery
	director := func(r *http.Request) {
		r.URL.Scheme = targeturl.Scheme
		r.URL.Host = targeturl.Host
		newpath := strings.TrimPrefix(r.URL.Path, path)
		r.URL.Path = singleJoiningSlash(targeturl.Path, newpath)
		if targetQuery == "" || r.URL.RawQuery == "" {
			r.URL.RawQuery = targetQuery + r.URL.RawQuery
		} else {
			r.URL.RawQuery = targetQuery + "&" + r.URL.RawQuery
		}
	}
	return &httputil.ReverseProxy{Director: director}
}

// Assumes that the local docker image (imageSpec) exists and has been sync'd
// with the registry.
var dockerRun = func(imageSpec string, args ...string) (output string, err error) {
	targs := []string{"run", imageSpec}
	for _, s := range args {
		targs = append(targs, s)
	}
	docker := exec.Command("docker", targs...)
	var outputBytes []byte
	outputBytes, err = docker.Output()
	if err != nil {
		return
	}
	output = string(outputBytes)
	return
}

type uidgid struct {
	uid int
	gid int
}

var userSpecCache struct {
	lookup map[string]uidgid
	sync.Mutex
}

func init() {
	userSpecCache.lookup = make(map[string]uidgid)
}

// Assumes that the local docker image (imageSpec) exists and has been sync'd
// with the registry.
func getInternalImageIDs(userSpec, imageSpec string) (uid, gid int, err error) {

	userSpecCache.Lock()
	defer userSpecCache.Unlock()

	key := userSpec + "!" + imageSpec
	if val, found := userSpecCache.lookup[key]; found {
		return val.uid, val.gid, nil
	}

	var output string
	// explicitly ignoring errors because of -rm under load
	output, _ = dockerRun(imageSpec, "/bin/sh", "-c",
		fmt.Sprintf(`touch test.txt && chown %s test.txt && ls -ln test.txt | awk '{ print $3, $4 }'`,
			userSpec))

	s := strings.TrimSpace(string(output))
	pattern := regexp.MustCompile(`^\d+ \d+$`)

	if !pattern.MatchString(s) {
		err = fmt.Errorf("unexpected output from getInternalImageIDs: %s", s)
		return
	}
	fields := strings.Fields(s)
	if len(fields) != 2 {
		err = fmt.Errorf("unexpected number of fields from container spec: %s", fields)
		return
	}
	uid, err = strconv.Atoi(fields[0])
	if err != nil {
		return
	}
	gid, err = strconv.Atoi(fields[1])
	if err != nil {
		return
	}
	// cache the results
	userSpecCache.lookup[key] = uidgid{uid: uid, gid: gid}
	time.Sleep(time.Second)
	return
}

var createVolumeDirMutex sync.Mutex

// createVolumeDir() creates a directory on the running host using the user ids
// found within the specified image. For example, it can create a directory owned
// by the mysql user (as seen by the container) despite there being no mysql user
// on the host system.
// Assumes that the local docker image (imageSpec) exists and has been sync'd
// with the registry.
func createVolumeDir(hostPath, containerSpec, imageSpec, userSpec, permissionSpec string) error {

	createVolumeDirMutex.Lock()
	defer createVolumeDirMutex.Unlock()

	dotfile := ".serviced.initialized"
	dotfileHostPath := path.Join(hostPath, dotfile)
	_, err := os.Stat(dotfileHostPath)
	if err == nil {
		glog.V(2).Infof("volume initialized earlier for src:%s dst:%s image:%s user:%s perm:%s", hostPath, containerSpec, imageSpec, userSpec, permissionSpec)
		return nil
	}

	starttime := time.Now()

	// FIXME: this relies on the underlying container to have /bin/sh that supports
	// some advanced shell options. This should be rewriten so that serviced injects itself in the
	// container and performs the operations using only go!
	// the file globbing checks that /mnt/dfs is empty before the copy - should initially be empty
	//    we don't want the copy to occur multiple times if restarting services.

	var output []byte
	command := [...]string{
		"docker", "run",
		"--rm",
		"-v", hostPath + ":/mnt/dfs",
		imageSpec,
		"/bin/bash", "-c",
		fmt.Sprintf(`
chown %s /mnt/dfs && \
chmod %s /mnt/dfs && \
shopt -s nullglob && \
shopt -s dotglob && \
files=(/mnt/dfs/*) && \
if [ ! -d "%s" ]; then
	echo "ERROR: srcdir %s does not exist in container"
	exit 2
elif [ ${#files[@]} -eq 0 ]; then
	cp -rp %s/* /mnt/dfs/
fi
touch /mnt/dfs/%s
sleep 5s
`, userSpec, permissionSpec, containerSpec, containerSpec, containerSpec, dotfile),
	}

	for i := 0; i < 1; i++ {
		docker := exec.Command(command[0], command[1:]...)
		output, err = docker.CombinedOutput()
		if err == nil {
			duration := time.Now().Sub(starttime)
			glog.V(2).Infof("volume init took %s for src:%s dst:%s image:%s user:%s perm:%s", duration, hostPath, containerSpec, imageSpec, userSpec, permissionSpec)
			return nil
		}
		time.Sleep(time.Second)
	}

	glog.Errorf("could not create host volume: %+v, %s", command, string(output))
	return err
}

// In the container
func AddToEtcHosts(host, ip string) error {
	// First make sure /etc/hosts is writeable
	command := []string{
		"/bin/bash", "-c", fmt.Sprintf(`
if [ -n "$(mount | grep /etc/hosts)" ]; then \
	cat /etc/hosts > /tmp/etchosts; \
	umount /etc/hosts; \
	mv /tmp/etchosts /etc/hosts; \
fi; \
echo "%s %s" >> /etc/hosts`, ip, host)}
	return exec.Command(command[0], command[1:]...).Run()
}
<commit_msg>fixes CC-757: replace sleep 5s with sync for optimized service volume initialization<commit_after>// Copyright 2014 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package agent implements a service that runs on a serviced node. It is
// responsible for ensuring that a particular node is running the correct services
// and reporting the state and health of those services back to the master
// serviced.

package node

import (
	"github.com/zenoss/glog"

	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"os/user"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
)

// validOwnerSpec returns true if the owner is specified in owner:group format and the
// identifiers are valid POSIX.1-2008 username and group strings, respectively.
func validOwnerSpec(owner string) bool {
	var pattern = regexp.MustCompile(`^[a-zA-Z]+[a-zA-Z0-9.-]*:[a-zA-Z]+[a-zA-Z0-9.-]*$`)
	return pattern.MatchString(owner)
}

// GetInterfaceIPAddress attempts to find the IP address based on interface name
func GetInterfaceIPAddress(_interface string) (string, error) {
	output, err := exec.Command("/sbin/ip", "-4", "-o", "addr").Output()
	if err != nil {
		return "", err
	}

	for _, line := range strings.Split(string(output), "\n") {
		fields := strings.Fields(line)
		if len(fields) < 4 {
			continue
		}

		if strings.HasPrefix(fields[1], _interface) {
			return strings.Split(fields[3], "/")[0], nil
		}
	}

	return "", fmt.Errorf("Unable to find ip for interface: %s", _interface)
}

// getIPAddrFromOutGoingConnection get the IP bound to the interface which
// handles the default route traffic.
func getIPAddrFromOutGoingConnection() (ip string, err error) {
	addr, err := net.ResolveUDPAddr("udp4", "8.8.8.8:53")
	if err != nil {
		return "", err
	}

	conn, err := net.DialUDP("udp4", nil, addr)
	if err != nil {
		return "", err
	}

	localAddr := conn.LocalAddr()
	parts := strings.Split(localAddr.String(), ":")
	return parts[0], nil
}

// ExecPath returns the path to the currently running executable.
func ExecPath() (string, string, error) {
	path, err := os.Readlink("/proc/self/exe")
	if err != nil {
		return "", "", err
	}
	return filepath.Dir(path), filepath.Base(path), nil
}

// DockerVersion contains the tuples that describe the version of docker.
type DockerVersion struct {
	Client []int
	Server []int
}

// equals compares two DockerVersion structs and returns true if they are equal.
func (a *DockerVersion) equals(b *DockerVersion) bool {
	if len(a.Client) != len(b.Client) {
		return false
	}
	for i, aI := range a.Client {
		if aI != b.Client[i] {
			return false
		}
	}
	if len(a.Server) != len(b.Server) {
		return false
	}
	for i, aI := range a.Server {
		if aI != b.Server[i] {
			return false
		}
	}
	return true
}

// GetDockerVersion returns docker version number.
func GetDockerVersion() (DockerVersion, error) {
	cmd := exec.Command("docker", "version")
	output, err := cmd.Output()
	if err != nil {
		return DockerVersion{}, err
	}
	return parseDockerVersion(string(output))
}

// parseDockerVersion parses the output of the 'docker version' commmand and
// returns a DockerVersion object.
func parseDockerVersion(output string) (version DockerVersion, err error) {

	for _, line := range strings.Split(output, "\n") {
		parts := strings.SplitN(line, ":", 2)
		if len(parts) < 2 {
			continue
		}
		if strings.HasPrefix(parts[0], "Client version") {
			a := strings.SplitN(strings.TrimSpace(parts[1]), "-", 2)
			b := strings.Split(a[0], ".")
			version.Client = make([]int, len(b))
			for i, v := range b {
				x, err := strconv.Atoi(v)
				if err != nil {
					return version, err
				}
				version.Client[i] = x
			}
		}
		if strings.HasPrefix(parts[0], "Server version") {
			a := strings.SplitN(strings.TrimSpace(parts[1]), "-", 2)
			b := strings.Split(a[0], ".")
			version.Server = make([]int, len(b))
			for i, v := range b {
				x, err := strconv.Atoi(v)
				if err != nil {
					return version, err
				}
				version.Server[i] = x
			}
		}
	}
	if len(version.Client) == 0 {
		return version, fmt.Errorf("no client version found")
	}
	if len(version.Server) == 0 {
		return version, fmt.Errorf("no server version found")
	}
	return version, nil
}

// CreateDirectory creates a directory using the given username as the owner and the
// given perm as the directory permission.
func CreateDirectory(path, username string, perm os.FileMode) error {
	user, err := user.Lookup(username)
	if err == nil {
		err = os.MkdirAll(path, perm)
		if err == nil || err == os.ErrExist {
			uid, _ := strconv.Atoi(user.Uid)
			gid, _ := strconv.Atoi(user.Gid)
			err = os.Chown(path, uid, gid)
		}
	}
	return err
}

// singleJoiningSlash joins a and b ensuring there is only a single /
// character between them.
func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

// NewReverseProxy differs from httputil.NewSingleHostReverseProxy in that it rewrites
// the path so that it does /not/ include the incoming path. e.g. request for
// "/mysvc/thing" when proxy is served from "/mysvc" means target is
// targeturl.Path + "/thing"; vs. httputil.NewSingleHostReverseProxy, in which
// it would be targeturl.Path + "/mysvc/thing".
func NewReverseProxy(path string, targeturl *url.URL) *httputil.ReverseProxy {
	targetQuery := targeturl.RawQuery
	director := func(r *http.Request) {
		r.URL.Scheme = targeturl.Scheme
		r.URL.Host = targeturl.Host
		newpath := strings.TrimPrefix(r.URL.Path, path)
		r.URL.Path = singleJoiningSlash(targeturl.Path, newpath)
		if targetQuery == "" || r.URL.RawQuery == "" {
			r.URL.RawQuery = targetQuery + r.URL.RawQuery
		} else {
			r.URL.RawQuery = targetQuery + "&" + r.URL.RawQuery
		}
	}
	return &httputil.ReverseProxy{Director: director}
}

// Assumes that the local docker image (imageSpec) exists and has been sync'd
// with the registry.
var dockerRun = func(imageSpec string, args ...string) (output string, err error) {
	targs := []string{"run", imageSpec}
	for _, s := range args {
		targs = append(targs, s)
	}
	docker := exec.Command("docker", targs...)
	var outputBytes []byte
	outputBytes, err = docker.Output()
	if err != nil {
		return
	}
	output = string(outputBytes)
	return
}

type uidgid struct {
	uid int
	gid int
}

var userSpecCache struct {
	lookup map[string]uidgid
	sync.Mutex
}

func init() {
	userSpecCache.lookup = make(map[string]uidgid)
}

// Assumes that the local docker image (imageSpec) exists and has been sync'd
// with the registry.
func getInternalImageIDs(userSpec, imageSpec string) (uid, gid int, err error) {

	userSpecCache.Lock()
	defer userSpecCache.Unlock()

	key := userSpec + "!" + imageSpec
	if val, found := userSpecCache.lookup[key]; found {
		return val.uid, val.gid, nil
	}

	var output string
	// explicitly ignoring errors because of -rm under load
	output, _ = dockerRun(imageSpec, "/bin/sh", "-c",
		fmt.Sprintf(`touch test.txt && chown %s test.txt && ls -ln test.txt | awk '{ print $3, $4 }'`,
			userSpec))

	s := strings.TrimSpace(string(output))
	pattern := regexp.MustCompile(`^\d+ \d+$`)

	if !pattern.MatchString(s) {
		err = fmt.Errorf("unexpected output from getInternalImageIDs: %s", s)
		return
	}
	fields := strings.Fields(s)
	if len(fields) != 2 {
		err = fmt.Errorf("unexpected number of fields from container spec: %s", fields)
		return
	}
	uid, err = strconv.Atoi(fields[0])
	if err != nil {
		return
	}
	gid, err = strconv.Atoi(fields[1])
	if err != nil {
		return
	}
	// cache the results
	userSpecCache.lookup[key] = uidgid{uid: uid, gid: gid}
	time.Sleep(time.Second)
	return
}

var createVolumeDirMutex sync.Mutex

// createVolumeDir() creates a directory on the running host using the user ids
// found within the specified image. For example, it can create a directory owned
// by the mysql user (as seen by the container) despite there being no mysql user
// on the host system.
// Assumes that the local docker image (imageSpec) exists and has been sync'd
// with the registry.
func createVolumeDir(hostPath, containerSpec, imageSpec, userSpec, permissionSpec string) error {

	createVolumeDirMutex.Lock()
	defer createVolumeDirMutex.Unlock()

	dotfile := ".serviced.initialized"
	dotfileHostPath := path.Join(hostPath, dotfile)
	_, err := os.Stat(dotfileHostPath)
	if err == nil {
		glog.V(2).Infof("volume initialized earlier for src:%s dst:%s image:%s user:%s perm:%s", hostPath, containerSpec, imageSpec, userSpec, permissionSpec)
		return nil
	}

	starttime := time.Now()

	// FIXME: this relies on the underlying container to have /bin/sh that supports
	// some advanced shell options. This should be rewriten so that serviced injects itself in the
	// container and performs the operations using only go!
	// the file globbing checks that /mnt/dfs is empty before the copy - should initially be empty
	//    we don't want the copy to occur multiple times if restarting services.

	var output []byte
	command := [...]string{
		"docker", "run",
		"--rm",
		"-v", hostPath + ":/mnt/dfs",
		imageSpec,
		"/bin/bash", "-c",
		fmt.Sprintf(`
chown %s /mnt/dfs && \
chmod %s /mnt/dfs && \
shopt -s nullglob && \
shopt -s dotglob && \
files=(/mnt/dfs/*) && \
if [ ! -d "%s" ]; then
	echo "ERROR: srcdir %s does not exist in container"
	exit 2
elif [ ${#files[@]} -eq 0 ]; then
	cp -rp %s/* /mnt/dfs/
fi
touch /mnt/dfs/%s
sync
`, userSpec, permissionSpec, containerSpec, containerSpec, containerSpec, dotfile),
	}

	for i := 0; i < 1; i++ {
		docker := exec.Command(command[0], command[1:]...)
		output, err = docker.CombinedOutput()
		if err == nil {
			duration := time.Now().Sub(starttime)
			glog.V(2).Infof("volume init took %s for src:%s dst:%s image:%s user:%s perm:%s", duration, hostPath, containerSpec, imageSpec, userSpec, permissionSpec)
			return nil
		}
		time.Sleep(time.Second)
	}

	glog.Errorf("could not create host volume: %+v, %s", command, string(output))
	return err
}

// In the container
func AddToEtcHosts(host, ip string) error {
	// First make sure /etc/hosts is writeable
	command := []string{
		"/bin/bash", "-c", fmt.Sprintf(`
if [ -n "$(mount | grep /etc/hosts)" ]; then \
	cat /etc/hosts > /tmp/etchosts; \
	umount /etc/hosts; \
	mv /tmp/etchosts /etc/hosts; \
fi; \
echo "%s %s" >> /etc/hosts`, ip, host)}
	return exec.Command(command[0], command[1:]...).Run()
}
<|endoftext|>


<commit_before>// 22 feb 2012

package main

import (
	"fmt"
	"os"
	"io"
	"strings"
	"hash"
	"hash/crc32"
	"crypto/md5"
	"crypto/sha1"
	"log"

	"github.com/andlabs/nointrochk/clrmamepro"
)

var folder, filename, basename string

func passesChecksum(method hash.Hash, mname string, expected string) bool {
	// according to the crypto hash testing code in the Go source code, this is what we do
	// thanks to #go-nuts for fixing a misunderstanding of that code that I had
	// thanks to rog in #go-nuts for the Copy() trick
	f, err := os.Open(filename)
	if err != nil {
		log.Fatalf("open %s to check %s failed: %v\n", filename, mname, err)
	}
	defer f.Close()
	io.Copy(method, f)
	if strings.ToUpper(fmt.Sprintf("%x", method.Sum(nil))) != strings.ToUpper(expected) {
		alert("BAD " + strings.ToUpper(mname))
		nbad++
		return false
	}
	return true
}

func do(b Block) {
	var good bool

	stats := b.Blocks[fROM]
	basename = stats.Texts[fFilename]
	filename = filepath.Join(folder, basename)
	good = fileExistsAndSameSize(stats.Texts[fSize])
	good = good && passesChecksum(crc32.NewIEEE(), "crc32", stats.Texts[fCRC32])
	good = good && passesChecksum(md5.New(), "md5", stats.Texts[fMD5])
	good = good && passesChecksum(sha1.New(), "sha1", stats.Texts[fSHA1])
	if good {
		alert("GOOD")
		ngood++
	}
}

func main() {
	d, err := OpenDatfile(os.Args[1])
	if err != nil {
		log.Fatalf("can't open datfile: %v\n",err)
	}
	folder = os.Args[2]
	for {
		b, err := d.GetBlock()
		if b == nil && err == nil {
			break
		}
		if err == nil {
			if b.Name == fGame {
				nroms++
				do(*b)
			}
		} else {
			log.Fatalf("error reading block: %v\n", err)
		}
	}
	fmt.Printf("%d ROMs, %d good, %d bad, %d missing (%f%% good)\n",
		nroms, ngood, nbad, nmiss,
		(float64(ngood) / float64(nroms)) * 100.0)
}
<commit_msg>Omit the old nointrochk.go from the build.<commit_after>// +build omit

// 22 feb 2012

package main

import (
	"fmt"
	"os"
	"io"
	"strings"
	"hash"
	"hash/crc32"
	"crypto/md5"
	"crypto/sha1"
	"log"

	"github.com/andlabs/nointrochk/clrmamepro"
)

var folder, filename, basename string

func passesChecksum(method hash.Hash, mname string, expected string) bool {
	// according to the crypto hash testing code in the Go source code, this is what we do
	// thanks to #go-nuts for fixing a misunderstanding of that code that I had
	// thanks to rog in #go-nuts for the Copy() trick
	f, err := os.Open(filename)
	if err != nil {
		log.Fatalf("open %s to check %s failed: %v\n", filename, mname, err)
	}
	defer f.Close()
	io.Copy(method, f)
	if strings.ToUpper(fmt.Sprintf("%x", method.Sum(nil))) != strings.ToUpper(expected) {
		alert("BAD " + strings.ToUpper(mname))
		nbad++
		return false
	}
	return true
}

func do(b Block) {
	var good bool

	stats := b.Blocks[fROM]
	basename = stats.Texts[fFilename]
	filename = filepath.Join(folder, basename)
	good = fileExistsAndSameSize(stats.Texts[fSize])
	good = good && passesChecksum(crc32.NewIEEE(), "crc32", stats.Texts[fCRC32])
	good = good && passesChecksum(md5.New(), "md5", stats.Texts[fMD5])
	good = good && passesChecksum(sha1.New(), "sha1", stats.Texts[fSHA1])
	if good {
		alert("GOOD")
		ngood++
	}
}

func main() {
	d, err := OpenDatfile(os.Args[1])
	if err != nil {
		log.Fatalf("can't open datfile: %v\n",err)
	}
	folder = os.Args[2]
	for {
		b, err := d.GetBlock()
		if b == nil && err == nil {
			break
		}
		if err == nil {
			if b.Name == fGame {
				nroms++
				do(*b)
			}
		} else {
			log.Fatalf("error reading block: %v\n", err)
		}
	}
	fmt.Printf("%d ROMs, %d good, %d bad, %d missing (%f%% good)\n",
		nroms, ngood, nbad, nmiss,
		(float64(ngood) / float64(nroms)) * 100.0)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"time"
)

func main() {
	device := flag.String("device", "eth0", "Device to sniff")
	snaplen := flag.Int("snaplen", 65535, "Snapshot length")
	promiscuous := flag.Bool("promiscuous", false, "Set promiscuous mode")
	timeout := flag.Duration("timeout", 30*time.Second, "Timeout")
	user := flag.String("user", "", "RabbitMQ user")
	passwd := flag.String("passwd", "", "RabbitMQ password")
	server := flag.String("server", "", "RabbitMQ server")
	exchange := flag.String("exchange", "", "RabbitMQ exchange name")
	filter := flag.String("filter", "", "Berkley Packet Filter (BPF)")

	flag.Parse()

	// Start sniffing
	Run(*device, int32(*snaplen), *promiscuous, *timeout,
		*user, *passwd, *server, *exchange, *filter)
}
<commit_msg>Add version<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"time"
)

const version string = "0.1.0"

func main() {
	device := flag.String("device", "eth0", "Device to sniff")
	snaplen := flag.Int("snaplen", 65535, "Snapshot length")
	promiscuous := flag.Bool("promiscuous", false, "Set promiscuous mode")
	timeout := flag.Duration("timeout", 30*time.Second, "Timeout")
	user := flag.String("user", "", "RabbitMQ user")
	passwd := flag.String("passwd", "", "RabbitMQ password")
	server := flag.String("server", "", "RabbitMQ server")
	exchange := flag.String("exchange", "", "RabbitMQ exchange name")
	filter := flag.String("filter", "", "Berkley Packet Filter (BPF)")
	showVersion := flag.Bool("version", false, "Show version")

	flag.Parse()

	if *showVersion {
		fmt.Println(version)
		os.Exit(0)
	}

	// Start sniffing
	Run(*device, int32(*snaplen), *promiscuous, *timeout,
		*user, *passwd, *server, *exchange, *filter)
}
<|endoftext|>


<commit_before>package TeleGogo

import (
	"bytes"
	"encoding/json"
	"io"
	"mime/multipart"
	"os"
	"strconv"
)

// createInputFileBody reads a file and creates a multipart writer for POSTing with the file and
// the form name
func createInputFileBody(path string, formName string) (*multipart.Writer, *bytes.Buffer, error) {
	var buffer bytes.Buffer
	writer := multipart.NewWriter(&buffer)

	file, err := os.Open(path)
	if err != nil {
		return nil, nil, err
	}
	defer file.Close()

	fileUpload, err := writer.CreateFormFile(formName, path)
	if err != nil {
		return nil, nil, err
	}
	if _, err := io.Copy(fileUpload, file); err != nil {
		return nil, nil, err
	}
	return writer, &buffer, nil
}

// SendPhotoArgs represents the optional and required arguments for the SendPhoto method
type SendPhotoArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// PhotoID Required. Path to photo file on device. In one request only this, or PhotoID should be set.
	// Use this if you're uploading a new file. Use PhotoID if you want to send a photo that is already on
	// Telegram's servers, and you know it's file ID.
	PhotoPath string `json:"-"`
	// FileID Required. Unique ID for a photo file on Telegram's servers. In one request only this,
	// or PhotoPath should be set. Use this if you want to send a photo that is already on Telegram's
	// servers and you know it's file ID. Otherwise, use PhotoPath.
	FileID string `json:"photo"`
	// Caption Optional. Photo caption. 0-200 chars.
	Caption string `json:"caption,omitempty"`
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkup Optional. Additional interface options. A JSON-serialized object for an inline keyboard,
	// custom reply keyboard, instructions to hide reply keyboard or to force a reply from the user.
	ReplyMarkup string `json:"reply_markup,omitempty"`
}

func (a SendPhotoArgs) methodName() string {
	return "sendPhoto"
}

func (a SendPhotoArgs) toJSON() ([]byte, error) {
	return json.Marshal(a)
}

func (a SendPhotoArgs) toMultiPart() (*multipart.Writer, *bytes.Buffer, error) {
	writer, buffer, err := createInputFileBody(a.PhotoPath, "photo")
	if err != nil {
		return nil, nil, err
	}
	/* Add ChatID */
	writer.WriteField("chat_id", a.ChatID)

	/* Optional args */
	if a.Caption != "" {
		writer.WriteField("caption", a.Caption)
	}
	if a.DisableNotification == true {
		writer.WriteField("disable_notification", "true")
	}
	if a.ReplyMarkup != "" {
		writer.WriteField("reply_markup", a.ReplyMarkup)
	}
	if a.ReplyToMessageID != 0 {
		writer.WriteField("reply_to_message_id", strconv.Itoa(a.ReplyToMessageID))
	}

	/* Close the writer; we're done with the body of the request. */
	if err = writer.Close(); err != nil {
		return nil, nil, err
	}
	return writer, buffer, nil
}

// SendAudioArgs represents the optional and required arguments for the SendAudio method
type SendAudioArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// AudioPath Required. Path to audio file on device IF uploading a new file. If you are trying to
	// send an audio file which is already on Telegram's servers, please keep this field empty and
	// use the AudioFileID field.
	AudioPath string `json:"-"`
	// AudioFileID Required. File ID of the audio file you want to send. If you are trying to
	// send a new audio file which is not already on Telegram's servers that you know of, please keep
	// this field empty and use the AudioPath field instead
	AudioFileID string `json:"audio"`
	// Duration Optional. Duration of the audio in seconds
	Duration int `json:"duration,omitempty"`
	// Performer Optional.
	Performer string `json:"performer,omitempty"`
	// Title Optional. Track name
	Title string `json:"title,omitempty"`
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkup Optional. Additional interface options. A JSON-serialized object for an inline keyboard,
	// custom reply keyboard, instructions to hide reply keyboard or to force a reply from the user.
	ReplyMarkup string `json:"reply_markup,omitempty"`
}

func (a SendAudioArgs) toMultiPart() (*multipart.Writer, *bytes.Buffer, error) {
	writer, buffer, err := createInputFileBody(a.AudioPath, "audio")
	if err != nil {
		return nil, nil, err
	}

	/* Add ChatID */
	writer.WriteField("chat_id", a.ChatID)

	/* Optional args */
	if a.Duration != 0 {
		writer.WriteField("duration", strconv.Itoa(a.Duration))
	}
	if a.Performer != "" {
		writer.WriteField("performer", a.Performer)
	}
	if a.Title != "" {
		writer.WriteField("title", a.Title)
	}
	if a.DisableNotification == true {
		writer.WriteField("disable_notification", "true")
	}
	if a.ReplyMarkup != "" {
		writer.WriteField("reply_markup", a.ReplyMarkup)
	}
	if a.ReplyToMessageID != 0 {
		writer.WriteField("reply_to_message_id", strconv.Itoa(a.ReplyToMessageID))
	}

	/* Close the writer; we're done with the body of the request. */
	if err = writer.Close(); err != nil {
		return nil, nil, err
	}

	return writer, buffer, nil
}

func (a SendAudioArgs) methodName() string {
	return "sendAudio"
}

func (a SendAudioArgs) toJSON() ([]byte, error) {
	return json.Marshal(a)
}

// SendDocumentArgs represents the optional and required arguments for the SendDocument method
type SendDocumentArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// DocumentPath Required. Use if you want to upload a new file to Telegram. If you are looking to send
	// a document that already exists on Telegram's servers and you know it's file ID, use DocumentFileID
	// instead. Only one of these fields may be filled.
	DocumentPath string `json:"-"`
	// DocumentFileID Required. Use if you want to send a document that already exists on Telegram's servers
	// and you know it's file ID. If you want to upload a new file, use DocumentPath instead. Only one of
	// these fields may be filled.
	DocumentFileID string `json:"document"`
	// Caption Optional. Document caption. 0-200 characters
	Caption string `json:"caption,omitempty"`
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkup Optional. Additional interface options. A JSON-serialized object for an inline keyboard,
	// custom reply keyboard, instructions to hide reply keyboard or to force a reply from the user.
	ReplyMarkup string `json:"reply_markup"`
}

func (a SendDocumentArgs) methodName() string {
	return "sendDocument"
}

func (a SendDocumentArgs) toMultiPart() (*multipart.Writer, *bytes.Buffer, error) {
	writer, buffer, err := createInputFileBody(a.DocumentPath, "document")
	if err != nil {
		return nil, nil, err
	}
	/* Add ChatID */
	writer.WriteField("chat_id", a.ChatID)

	/* Optional args */
	if a.Caption != "" {
		writer.WriteField("caption", a.Caption)
	}
	if a.DisableNotification == true {
		writer.WriteField("disable_notification", "true")
	}
	if a.ReplyToMessageID != 0 {
		writer.WriteField("reply_to_message_id", strconv.Itoa(a.ReplyToMessageID))
	}
	if a.ReplyMarkup != "" {
		writer.WriteField("reply_markup", a.ReplyMarkup)
	}

	/* Close the writer; we're done with the body of the request. */
	if err = writer.Close(); err != nil {
		return nil, nil, err
	}

	return writer, buffer, nil
}

func (a SendDocumentArgs) toJSON() ([]byte, error) {
	return json.Marshal(a)
}

// SendStickerArgs represents the optional and required arguments for the SendSticker method
type SendStickerArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// StickerPath Required. Use if uploading a sticker file (.webp). Represents the path to a sticker file.
	// Only use if sending a new sticker. If sending an existing sticker, please use StickerPath.
	StickerPath string `json:"-"`
	// StickerID Required. Use if sending a sticker file that already exists. Represents the FileID of
	// the sticker. If you want to send a new sticker from a file, please use StickerPath.
	StickerID string `json:"sticker"`
	//DisableNotification Optional. Sends a message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkupAdditional interface options. A JSON-serialized object for an inline keyboard,
	// custom reply keyboard, instructions to hide reply keyboard or to force a reply from the user.
	ReplyMarkup string `json:"reply_markup,omitempty"`
}

func (a SendStickerArgs) toJSON() ([]byte, error) {
	return json.Marshal(a)
}

func (a SendStickerArgs) methodName() string {
	return "sendSticker"
}

func (a SendStickerArgs) toMultiPart() (*multipart.Writer, *bytes.Buffer, error) {
	writer, buffer, err := createInputFileBody(a.StickerPath, "sticker")
	if err != nil {
		return nil, nil, err
	}
	/* Add ChatID */
	writer.WriteField("chat_id", a.ChatID)

	/* Optional args */
	if a.DisableNotification == true {
		writer.WriteField("disable_notification", "true")
	}
	if a.ReplyToMessageID != 0 {
		writer.WriteField("reply_to_message_id", strconv.Itoa(a.ReplyToMessageID))
	}
	if a.ReplyMarkup != "" {
		writer.WriteField("reply_markup", a.ReplyMarkup)
	}

	/* Close the writer; we're done with the body of the request. */
	if err = writer.Close(); err != nil {
		return nil, nil, err
	}

	return writer, buffer, nil
}

// SendVideoArgs represents the optional and required arguments for the SendVideo method
type SendVideoArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// VideoPath Required. File path of the video you want to send. If you want to send a video that already
	// exists on Telegram's servers and you know it's file ID, use VideoID instead.
	VideoPath string `json:"-"`
	// VideoID Required. File ID of the video you want to send. TO upload a new video file, please use
	// VideoPath instead.
	VideoID string `json:"video"`
	// Duration Optional. Duration of sent video in seconds
	Duration int `json:"duration, omitempty"`
	// Width Optional. Video width
	Width int `json:"width,omitempty"`
	// Height Optional. Video height
	Height int `json:"height,omitempty"`
	// Caption Optional. Video caption, 0-200 characters
	Caption string `json:"caption"`
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkup Optional. Additional interface options.
	ReplyMarkup string `json:"reply_markup,omitempty"`
}

func (a SendVideoArgs) toJSON() ([]byte, error) {
	return json.Marshal(a)
}

func (a SendVideoArgs) methodName() string {
	return "sendVideo"
}

func (a SendVideoArgs) toMultiPart() (*multipart.Writer, *bytes.Buffer, error) {
	writer, buffer, err := createInputFileBody(a.VideoPath, "video")
	if err != nil {
		return nil, nil, err
	}
	/* Add ChatID */
	writer.WriteField("chat_id", a.ChatID)

	/* Optional args */
	if a.Duration != 0 {
		writer.WriteField("duration", strconv.Itoa(a.Duration))
	}
	if a.Width != 0 {
		writer.WriteField("width", strconv.Itoa(a.Width))
	}
	if a.Height != 0 {
		writer.WriteField("height", strconv.Itoa(a.Height))
	}
	if a.Caption != "" {
		writer.WriteField("caption", a.Caption)
	}
	if a.DisableNotification == true {
		writer.WriteField("disable_notification", "true")
	}
	if a.ReplyToMessageID != 0 {
		writer.WriteField("reply_to_message_id", strconv.Itoa(a.ReplyToMessageID))
	}
	if a.ReplyMarkup != "" {
		writer.WriteField("reply_markup", a.ReplyMarkup)
	}

	/* Close the writer; we're done with the body of the request. */
	if err = writer.Close(); err != nil {
		return nil, nil, err
	}

	return writer, buffer, nil
}

type fileUploader interface {
	toMultiPart() (*multipart.Writer, *bytes.Buffer, error)
	methodName() string
}

type jsonUploader interface {
	toJSON() ([]byte, error)
	methodName() string
}
<commit_msg>added SendVoiceArgs<commit_after>package TeleGogo

import (
	"bytes"
	"encoding/json"
	"io"
	"mime/multipart"
	"os"
	"strconv"
)

// createInputFileBody reads a file and creates a multipart writer for POSTing with the file and
// the form name
func createInputFileBody(path string, formName string) (*multipart.Writer, *bytes.Buffer, error) {
	var buffer bytes.Buffer
	writer := multipart.NewWriter(&buffer)

	file, err := os.Open(path)
	if err != nil {
		return nil, nil, err
	}
	defer file.Close()

	fileUpload, err := writer.CreateFormFile(formName, path)
	if err != nil {
		return nil, nil, err
	}
	if _, err := io.Copy(fileUpload, file); err != nil {
		return nil, nil, err
	}
	return writer, &buffer, nil
}

// SendPhotoArgs represents the optional and required arguments for the SendPhoto method
type SendPhotoArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// PhotoID Required. Path to photo file on device. In one request only this, or PhotoID should be set.
	// Use this if you're uploading a new file. Use PhotoID if you want to send a photo that is already on
	// Telegram's servers, and you know it's file ID.
	PhotoPath string `json:"-"`
	// FileID Required. Unique ID for a photo file on Telegram's servers. In one request only this,
	// or PhotoPath should be set. Use this if you want to send a photo that is already on Telegram's
	// servers and you know it's file ID. Otherwise, use PhotoPath.
	FileID string `json:"photo"`
	// Caption Optional. Photo caption. 0-200 chars.
	Caption string `json:"caption,omitempty"`
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkup Optional. Additional interface options. A JSON-serialized object for an inline keyboard,
	// custom reply keyboard, instructions to hide reply keyboard or to force a reply from the user.
	ReplyMarkup string `json:"reply_markup,omitempty"`
}

func (a SendPhotoArgs) methodName() string {
	return "sendPhoto"
}

func (a SendPhotoArgs) toJSON() ([]byte, error) {
	return json.Marshal(a)
}

func (a SendPhotoArgs) toMultiPart() (*multipart.Writer, *bytes.Buffer, error) {
	writer, buffer, err := createInputFileBody(a.PhotoPath, "photo")
	if err != nil {
		return nil, nil, err
	}
	/* Add ChatID */
	writer.WriteField("chat_id", a.ChatID)

	/* Optional args */
	if a.Caption != "" {
		writer.WriteField("caption", a.Caption)
	}
	if a.DisableNotification == true {
		writer.WriteField("disable_notification", "true")
	}
	if a.ReplyMarkup != "" {
		writer.WriteField("reply_markup", a.ReplyMarkup)
	}
	if a.ReplyToMessageID != 0 {
		writer.WriteField("reply_to_message_id", strconv.Itoa(a.ReplyToMessageID))
	}

	/* Close the writer; we're done with the body of the request. */
	if err = writer.Close(); err != nil {
		return nil, nil, err
	}
	return writer, buffer, nil
}

// SendAudioArgs represents the optional and required arguments for the SendAudio method
type SendAudioArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// AudioPath Required. Path to audio file on device IF uploading a new file. If you are trying to
	// send an audio file which is already on Telegram's servers, please keep this field empty and
	// use the AudioFileID field.
	AudioPath string `json:"-"`
	// AudioFileID Required. File ID of the audio file you want to send. If you are trying to
	// send a new audio file which is not already on Telegram's servers that you know of, please keep
	// this field empty and use the AudioPath field instead
	AudioFileID string `json:"audio"`
	// Duration Optional. Duration of the audio in seconds
	Duration int `json:"duration,omitempty"`
	// Performer Optional.
	Performer string `json:"performer,omitempty"`
	// Title Optional. Track name
	Title string `json:"title,omitempty"`
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkup Optional. Additional interface options. A JSON-serialized object for an inline keyboard,
	// custom reply keyboard, instructions to hide reply keyboard or to force a reply from the user.
	ReplyMarkup string `json:"reply_markup,omitempty"`
}

func (a SendAudioArgs) toMultiPart() (*multipart.Writer, *bytes.Buffer, error) {
	writer, buffer, err := createInputFileBody(a.AudioPath, "audio")
	if err != nil {
		return nil, nil, err
	}

	/* Add ChatID */
	writer.WriteField("chat_id", a.ChatID)

	/* Optional args */
	if a.Duration != 0 {
		writer.WriteField("duration", strconv.Itoa(a.Duration))
	}
	if a.Performer != "" {
		writer.WriteField("performer", a.Performer)
	}
	if a.Title != "" {
		writer.WriteField("title", a.Title)
	}
	if a.DisableNotification == true {
		writer.WriteField("disable_notification", "true")
	}
	if a.ReplyMarkup != "" {
		writer.WriteField("reply_markup", a.ReplyMarkup)
	}
	if a.ReplyToMessageID != 0 {
		writer.WriteField("reply_to_message_id", strconv.Itoa(a.ReplyToMessageID))
	}

	/* Close the writer; we're done with the body of the request. */
	if err = writer.Close(); err != nil {
		return nil, nil, err
	}

	return writer, buffer, nil
}

func (a SendAudioArgs) methodName() string {
	return "sendAudio"
}

func (a SendAudioArgs) toJSON() ([]byte, error) {
	return json.Marshal(a)
}

// SendDocumentArgs represents the optional and required arguments for the SendDocument method
type SendDocumentArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// DocumentPath Required. Use if you want to upload a new file to Telegram. If you are looking to send
	// a document that already exists on Telegram's servers and you know it's file ID, use DocumentFileID
	// instead. Only one of these fields may be filled.
	DocumentPath string `json:"-"`
	// DocumentFileID Required. Use if you want to send a document that already exists on Telegram's servers
	// and you know it's file ID. If you want to upload a new file, use DocumentPath instead. Only one of
	// these fields may be filled.
	DocumentFileID string `json:"document"`
	// Caption Optional. Document caption. 0-200 characters
	Caption string `json:"caption,omitempty"`
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkup Optional. Additional interface options. A JSON-serialized object for an inline keyboard,
	// custom reply keyboard, instructions to hide reply keyboard or to force a reply from the user.
	ReplyMarkup string `json:"reply_markup"`
}

func (a SendDocumentArgs) methodName() string {
	return "sendDocument"
}

func (a SendDocumentArgs) toMultiPart() (*multipart.Writer, *bytes.Buffer, error) {
	writer, buffer, err := createInputFileBody(a.DocumentPath, "document")
	if err != nil {
		return nil, nil, err
	}
	/* Add ChatID */
	writer.WriteField("chat_id", a.ChatID)

	/* Optional args */
	if a.Caption != "" {
		writer.WriteField("caption", a.Caption)
	}
	if a.DisableNotification == true {
		writer.WriteField("disable_notification", "true")
	}
	if a.ReplyToMessageID != 0 {
		writer.WriteField("reply_to_message_id", strconv.Itoa(a.ReplyToMessageID))
	}
	if a.ReplyMarkup != "" {
		writer.WriteField("reply_markup", a.ReplyMarkup)
	}

	/* Close the writer; we're done with the body of the request. */
	if err = writer.Close(); err != nil {
		return nil, nil, err
	}

	return writer, buffer, nil
}

func (a SendDocumentArgs) toJSON() ([]byte, error) {
	return json.Marshal(a)
}

// SendStickerArgs represents the optional and required arguments for the SendSticker method
type SendStickerArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// StickerPath Required. Use if uploading a sticker file (.webp). Represents the path to a sticker file.
	// Only use if sending a new sticker. If sending an existing sticker, please use StickerPath.
	StickerPath string `json:"-"`
	// StickerID Required. Use if sending a sticker file that already exists. Represents the FileID of
	// the sticker. If you want to send a new sticker from a file, please use StickerPath.
	StickerID string `json:"sticker"`
	//DisableNotification Optional. Sends a message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkupAdditional interface options. A JSON-serialized object for an inline keyboard,
	// custom reply keyboard, instructions to hide reply keyboard or to force a reply from the user.
	ReplyMarkup string `json:"reply_markup,omitempty"`
}

func (a SendStickerArgs) toJSON() ([]byte, error) {
	return json.Marshal(a)
}

func (a SendStickerArgs) methodName() string {
	return "sendSticker"
}

func (a SendStickerArgs) toMultiPart() (*multipart.Writer, *bytes.Buffer, error) {
	writer, buffer, err := createInputFileBody(a.StickerPath, "sticker")
	if err != nil {
		return nil, nil, err
	}
	/* Add ChatID */
	writer.WriteField("chat_id", a.ChatID)

	/* Optional args */
	if a.DisableNotification == true {
		writer.WriteField("disable_notification", "true")
	}
	if a.ReplyToMessageID != 0 {
		writer.WriteField("reply_to_message_id", strconv.Itoa(a.ReplyToMessageID))
	}
	if a.ReplyMarkup != "" {
		writer.WriteField("reply_markup", a.ReplyMarkup)
	}

	/* Close the writer; we're done with the body of the request. */
	if err = writer.Close(); err != nil {
		return nil, nil, err
	}

	return writer, buffer, nil
}

// SendVideoArgs represents the optional and required arguments for the SendVideo method
type SendVideoArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// VideoPath Required. File path of the video you want to send. If you want to send a video that already
	// exists on Telegram's servers and you know it's file ID, use VideoID instead.
	VideoPath string `json:"-"`
	// VideoID Required. File ID of the video you want to send. TO upload a new video file, please use
	// VideoPath instead.
	VideoID string `json:"video"`
	// Duration Optional. Duration of sent video in seconds
	Duration int `json:"duration, omitempty"`
	// Width Optional. Video width
	Width int `json:"width,omitempty"`
	// Height Optional. Video height
	Height int `json:"height,omitempty"`
	// Caption Optional. Video caption, 0-200 characters
	Caption string `json:"caption"`
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkup Optional. Additional interface options.
	ReplyMarkup string `json:"reply_markup,omitempty"`
}

func (a SendVideoArgs) toJSON() ([]byte, error) {
	return json.Marshal(a)
}

func (a SendVideoArgs) methodName() string {
	return "sendVideo"
}

func (a SendVideoArgs) toMultiPart() (*multipart.Writer, *bytes.Buffer, error) {
	writer, buffer, err := createInputFileBody(a.VideoPath, "video")
	if err != nil {
		return nil, nil, err
	}
	/* Add ChatID */
	writer.WriteField("chat_id", a.ChatID)

	/* Optional args */
	if a.Duration != 0 {
		writer.WriteField("duration", strconv.Itoa(a.Duration))
	}
	if a.Width != 0 {
		writer.WriteField("width", strconv.Itoa(a.Width))
	}
	if a.Height != 0 {
		writer.WriteField("height", strconv.Itoa(a.Height))
	}
	if a.Caption != "" {
		writer.WriteField("caption", a.Caption)
	}
	if a.DisableNotification == true {
		writer.WriteField("disable_notification", "true")
	}
	if a.ReplyToMessageID != 0 {
		writer.WriteField("reply_to_message_id", strconv.Itoa(a.ReplyToMessageID))
	}
	if a.ReplyMarkup != "" {
		writer.WriteField("reply_markup", a.ReplyMarkup)
	}

	/* Close the writer; we're done with the body of the request. */
	if err = writer.Close(); err != nil {
		return nil, nil, err
	}

	return writer, buffer, nil
}

// SendVoiceArgs represents the optional and required arguments for the SendVoice method
type SendVoiceArgs struct {
	// ChatID Required. Unique identifier for the target chat or username of the target channel
	// (in the format @channelusername)
	ChatID string `json:"chat_id"`
	// VoicePath Required. File path of the voice you want to send. If you want to send a voice that already
	// exists on Telegram's servers and you know it's file ID, use VoiceID instead.
	VoicePath string `json:"-"`
	// VoiceID Required. File ID of the voice you want to send. To upload a new voice file, please use
	// VoicePath instead.
	VoiceID string `json:"voice"`
	// Duration Optional. Duration of sent audio in seconds
	Duration int `json:"duration"`
	// DisableNotification Optional. Sends the message silently.
	DisableNotification bool `json:"disable_notification,omitempty"`
	// ReplyToMessageID Optional. If the message is a reply, ID of the original message
	ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
	// ReplyMarkup Optional. Additional interface options.
	ReplyMarkup string `json:"reply_markup,omitempty"`
}

type fileUploader interface {
	toMultiPart() (*multipart.Writer, *bytes.Buffer, error)
	methodName() string
}

type jsonUploader interface {
	toJSON() ([]byte, error)
	methodName() string
}
<|endoftext|>


<commit_before>package routers

import (
	"github.com/alex1sz/shotcharter-go/controllers"
	"github.com/gorilla/mux"
)

func SetGameRoutes(router *mux.Router) *mux.Router {
	gameRouter := mux.NewRouter()
	subRouter := gameRouter.PathPrefix("/games").Subrouter()

	subRouter.HandleFunc("/{id}", controllers.GetGameByID).Methods("GET")

	return router
}
<commit_msg>Fix game router<commit_after>package routers

import (
	"github.com/alex1sz/shotcharter-go/controllers"
	"github.com/gorilla/mux"
)

func SetGameRoutes(router *mux.Router) *mux.Router {
	gameRouter := mux.NewRouter()
	subRouter := gameRouter.PathPrefix("/games").Subrouter()

	subRouter.HandleFunc("/{id}", controllers.GetGameByID).Methods("GET")

	return gameRouter
}
<|endoftext|>