<commit_before>package ics

import (
	"encoding/base64"
	"errors"
	"io"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/MJKWoolnough/parser"
)

type Binary []byte

func (b *Binary) Decode(params map[string]string, data string) error {
	if params["ENCODING"] != "BASE64" {
		return ErrInvalidEncoding
	}
	cb, err := base64.StdEncoding.DecodeString(data)
	*b = cb
	return err
}

func (b *Binary) Encode(w io.Writer) {
	e := base64.NewEncoder(base64.StdEncoding, w)
	e.Write([]byte(*b))
	e.Close()
}

type Boolean bool

func (b *Boolean) Decode(params map[string]string, data string) error {
	cb, err := strconv.ParseBool(data)
	*b = cb
	return err
}

var (
	booleanTrue  = [...]byte{'T', 'R', 'U', 'E'}
	booleanFalse = [...]byte{'F', 'A', 'L', 'S', 'E'}
)

func (b *Boolean) Encode(w io.Writer) {
	if *b {
		w.Write(booleanTrue[:])
	} else {
		w.Write(booleanFalse[:])
	}
}

type CalAddress struct {
	URI
}

type Date struct {
	time.Time
}

func (d *Date) Decode(params map[string]string, data string) error {
	t, err := time.Parse("20060102", data)
	if err != nil {
		return err
	}
	d.Time = t
	return nil
}

func (d *Date) Encode(w io.Writer) {
	b := make([]byte, 0, 8)
	w.Write([]byte(d.AppendFormat(b, "20060102")))
}

type DateTime struct {
	time.Time
}

func (d *DateTime) Decode(params map[string]string, data string) error {
	if tz, ok := params["TZID"]; ok {
		l, err := time.LoadLocation(tz)
		if err != nil {
			return err
		}
		t, err := time.ParseInLocation("20060102T150405", data, l)
		if err != nil {
			return err
		}
		d.Time = t
	} else if len(data) > 0 && data[len(data)-1] == 'Z' {
		t, err := time.ParseInLocation("20060102T150405Z", data, time.UTC)
		if err != nil {
			return err
		}
		d.Time = t
	} else {
		t, err := time.ParseInLocation("20060102T150405", data, time.Local)
		if err != nil {
			return err
		}
		d.Time = t
	}
	return nil
}

func (d *DateTime) Encode(w io.Writer) {
	b := make([]byte, 0, 16)
	switch d.Location() {
	case time.UTC:
		b = d.AppendFormat(b, "20060102T150405Z")
	case time.Local:
		b = d.AppendFormat(b, "20060102T150405")
	default:
		b = d.AppendFormat(b, "20060102T150405")
	}
	w.Write(b)
}

type Duration struct {
	Negative                             bool
	Weeks, Days, Hours, Minutes, Seconds uint
}

func (d *Duration) Decode(params map[string]string, data string) error {
	t := parser.NewStringTokeniser(data)
	var neg bool
	if t.Accept("-") {
		neg = true
	} else {
		t.Accept("+")
	}
	if !t.Accept("P") {
		return ErrInvalidDuration
	}
	var level uint8
	for t.Peek() != -1 {
		t.Get()
		mode := t.AcceptRun("0123456789")
		num, err := strconv.ParseUint(t.Get(), 10, 0)
		if err != nil {
			return err
		}
		switch mode {
		case 'W':
			t.Accept("W")
			if t.Peek() != -1 {
				return ErrInvalidDuration
			}
			d.Weeks = num
			return nil
		case 'D':
			if level > 0 {
				return ErrInvalidDuration
			}
			t.Accept("D")
			d.Days = num
			level = 1
		case 'H':
			if level > 1 {
				return ErrInvalidDuration
			}
			t.Accept("H")
			d.Hours = num
			level = 2
		case 'M':
			if level > 2 {
				return ErrInvalidDuration
			}
			t.Accept("M")
			d.Minutes = num
			level = 3
		case 'S':
			if level > 3 {
				return ErrInvalidDuration
			}
			t.Accept("S")
			if t.Peek() != -1 {
				return ErrInvalidDuration
			}
			d.Seconds = num
		default:
			return ErrInvalidDuration
		}
	}
	if level == 0 {
		return ErrInvalidDuration
	}
	return nil
}

func itoa(i uint) []byte {
	if i == 0 {
		return []byte{'0'}
	}
	var digits [20]byte
	pos := 20
	for ; n > 0; n /= 10 {
		pos--
		digits[pos] = '0' + byte(n%10)
	}
	return digits[pos:]
}

func (d *Duration) Encode(w io.Writer) {
	data := make([]byte, 0, 64)
	if d.Negative {
		data = append(data, '-')
	}
	data = append(data, 'P')
	if d.Weeks != 0 {
		data = append(data, itoa(d.Weeks)...)
		data = append(data, 'W')
		return nil
	} else {
		if d.Days != 0 {
			data = append(data, itoa(d.Days)...)
			data = append(data, 'D')
		}
		if d.Days == 0 || (d.Hours != 0 || d.Minutes != 0 || d.Seconds != 0) {
			data = append(data, 'T')
			if d.Hours != 0 {
				data = append(data, itoa(d.Hours)...)
				data = append(data, 'H')
				if d.Minutes != 0 || d.Seconds != 0 {
					data = append(data, itoa(d.Minutes)...)
					data = append(data, 'M')
					if d.Seconds != 0 {
						data = append(data, itoa(d.Seconds)...)
						data = append(data, 'S')
					}
				}
			} else if d.Minutes != 0 {
				data = append(data, itoa(d.Minutes)...)
				data = append(data, 'M')
				if d.Seconds != 0 {
					data = append(data, itoa(d.Seconds)...)
					data = append(data, 'S')
				}
			} else {
				data = append(data, itoa(d.Seconds)...)
				data = append(data, 'S')
			}
		}
	}
	w.Write(data)
}

type Float float64

func (f *Float) Decode(params map[string]string, data string) error {
	cf, err := strconv.ParseFloat(data, 64)
	if err != nil {
		return err
	}
	*f = Float(cf)
	return nil
}

func (f *Float) Encode(w io.Writer) {
	w.Write([]byte(strconv.FormatFloat(float64(*f), 'f', 64)))
}

type Integer int32

func (i *Integer) Decode(params map[string]string, data string) error {
	ci, err := strconv.ParseInt(data, 10, 32)
	if err != nil {
		return err
	}
	*i = Integer(ci)
	return nil
}

func (i *Integer) Encode(w io.Writer) {
	w.Write([]byte(strconv.FormatInt(int64(*i), 10)))
}

type Period struct {
	Start, End DateTime
	Duration   Duration
}

func (p *Period) Decode(params map[string]string, data string) error {
	i := strings.IndexByte(data, '/')
	if i == -1 || len(data) == i+1 {
		return ErrInvalidPeriod
	}
	err := p.Start.Decode(params, data[:i])
	if err != nil {
		return err
	}
	if data[i+1] == 'P' || data[i+1] == '+' {
		return p.Duration.Decode(params, data[i+1:])
	}
	return p.End.Decode(params, data[i+1:])
}

func (p *Period) Encode(w io.Writer) {
	p.Start.Encode(w)
	if p.End.IsZero() {
		p.Duration.Encode(w)
	} else {
		p.End.Encode(w)
	}
}

type Recur struct {
}

func (r *Recur) Decode(params map[string]string, data string) error {
	return nil
}

func (r *Recur) Encode(w io.Writer) {

}

type Text string

func (t *Text) Decode(params map[string]string, data string) error {
	return nil
}

func (t *Text) Encode(w io.Writer) {

}

type Time struct {
	time.Time
}

func (t *Time) Decode(params map[string]string, data string) error {
	return nil
}

func (t *Time) Encode(w io.Writer) {

}

type URI struct {
	url.URL
}

func (u *URI) Decode(params map[string]string, data string) error {
	cu, err := url.Parse(data)
	if err != nil {
		return err
	}
	u.URL = cu
	return nil
}

func (u *URI) Encode(w io.Writer) {
	w.Write([]byte(u.URL.String()))
}

type UTCOffset int

func (u *UTCOffset) Decode(params map[string]string, data string) error {
	return nil
}

func (u *UTCOffset) Encode(w io.Writer) {

}

// Errors
var (
	ErrInvalidEncoding = errors.New("invalid binary encoding")
	ErrInvalidPeriod   = errors.New("invalid period")
	ErrInvalidDuration = errors.New("invalid duration")
)
<commit_msg>added missing seperator in period encoding<commit_after>package ics

import (
	"encoding/base64"
	"errors"
	"io"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/MJKWoolnough/parser"
)

type Binary []byte

func (b *Binary) Decode(params map[string]string, data string) error {
	if params["ENCODING"] != "BASE64" {
		return ErrInvalidEncoding
	}
	cb, err := base64.StdEncoding.DecodeString(data)
	*b = cb
	return err
}

func (b *Binary) Encode(w io.Writer) {
	e := base64.NewEncoder(base64.StdEncoding, w)
	e.Write([]byte(*b))
	e.Close()
}

type Boolean bool

func (b *Boolean) Decode(params map[string]string, data string) error {
	cb, err := strconv.ParseBool(data)
	*b = cb
	return err
}

var (
	booleanTrue  = [...]byte{'T', 'R', 'U', 'E'}
	booleanFalse = [...]byte{'F', 'A', 'L', 'S', 'E'}
)

func (b *Boolean) Encode(w io.Writer) {
	if *b {
		w.Write(booleanTrue[:])
	} else {
		w.Write(booleanFalse[:])
	}
}

type CalAddress struct {
	URI
}

type Date struct {
	time.Time
}

func (d *Date) Decode(params map[string]string, data string) error {
	t, err := time.Parse("20060102", data)
	if err != nil {
		return err
	}
	d.Time = t
	return nil
}

func (d *Date) Encode(w io.Writer) {
	b := make([]byte, 0, 8)
	w.Write([]byte(d.AppendFormat(b, "20060102")))
}

type DateTime struct {
	time.Time
}

func (d *DateTime) Decode(params map[string]string, data string) error {
	if tz, ok := params["TZID"]; ok {
		l, err := time.LoadLocation(tz)
		if err != nil {
			return err
		}
		t, err := time.ParseInLocation("20060102T150405", data, l)
		if err != nil {
			return err
		}
		d.Time = t
	} else if len(data) > 0 && data[len(data)-1] == 'Z' {
		t, err := time.ParseInLocation("20060102T150405Z", data, time.UTC)
		if err != nil {
			return err
		}
		d.Time = t
	} else {
		t, err := time.ParseInLocation("20060102T150405", data, time.Local)
		if err != nil {
			return err
		}
		d.Time = t
	}
	return nil
}

func (d *DateTime) Encode(w io.Writer) {
	b := make([]byte, 0, 16)
	switch d.Location() {
	case time.UTC:
		b = d.AppendFormat(b, "20060102T150405Z")
	case time.Local:
		b = d.AppendFormat(b, "20060102T150405")
	default:
		b = d.AppendFormat(b, "20060102T150405")
	}
	w.Write(b)
}

type Duration struct {
	Negative                             bool
	Weeks, Days, Hours, Minutes, Seconds uint
}

func (d *Duration) Decode(params map[string]string, data string) error {
	t := parser.NewStringTokeniser(data)
	var neg bool
	if t.Accept("-") {
		neg = true
	} else {
		t.Accept("+")
	}
	if !t.Accept("P") {
		return ErrInvalidDuration
	}
	var level uint8
	for t.Peek() != -1 {
		t.Get()
		mode := t.AcceptRun("0123456789")
		num, err := strconv.ParseUint(t.Get(), 10, 0)
		if err != nil {
			return err
		}
		switch mode {
		case 'W':
			t.Accept("W")
			if t.Peek() != -1 {
				return ErrInvalidDuration
			}
			d.Weeks = num
			return nil
		case 'D':
			if level > 0 {
				return ErrInvalidDuration
			}
			t.Accept("D")
			d.Days = num
			level = 1
		case 'H':
			if level > 1 {
				return ErrInvalidDuration
			}
			t.Accept("H")
			d.Hours = num
			level = 2
		case 'M':
			if level > 2 {
				return ErrInvalidDuration
			}
			t.Accept("M")
			d.Minutes = num
			level = 3
		case 'S':
			if level > 3 {
				return ErrInvalidDuration
			}
			t.Accept("S")
			if t.Peek() != -1 {
				return ErrInvalidDuration
			}
			d.Seconds = num
		default:
			return ErrInvalidDuration
		}
	}
	if level == 0 {
		return ErrInvalidDuration
	}
	return nil
}

func itoa(i uint) []byte {
	if i == 0 {
		return []byte{'0'}
	}
	var digits [20]byte
	pos := 20
	for ; n > 0; n /= 10 {
		pos--
		digits[pos] = '0' + byte(n%10)
	}
	return digits[pos:]
}

func (d *Duration) Encode(w io.Writer) {
	data := make([]byte, 0, 64)
	if d.Negative {
		data = append(data, '-')
	}
	data = append(data, 'P')
	if d.Weeks != 0 {
		data = append(data, itoa(d.Weeks)...)
		data = append(data, 'W')
		return nil
	} else {
		if d.Days != 0 {
			data = append(data, itoa(d.Days)...)
			data = append(data, 'D')
		}
		if d.Days == 0 || (d.Hours != 0 || d.Minutes != 0 || d.Seconds != 0) {
			data = append(data, 'T')
			if d.Hours != 0 {
				data = append(data, itoa(d.Hours)...)
				data = append(data, 'H')
				if d.Minutes != 0 || d.Seconds != 0 {
					data = append(data, itoa(d.Minutes)...)
					data = append(data, 'M')
					if d.Seconds != 0 {
						data = append(data, itoa(d.Seconds)...)
						data = append(data, 'S')
					}
				}
			} else if d.Minutes != 0 {
				data = append(data, itoa(d.Minutes)...)
				data = append(data, 'M')
				if d.Seconds != 0 {
					data = append(data, itoa(d.Seconds)...)
					data = append(data, 'S')
				}
			} else {
				data = append(data, itoa(d.Seconds)...)
				data = append(data, 'S')
			}
		}
	}
	w.Write(data)
}

type Float float64

func (f *Float) Decode(params map[string]string, data string) error {
	cf, err := strconv.ParseFloat(data, 64)
	if err != nil {
		return err
	}
	*f = Float(cf)
	return nil
}

func (f *Float) Encode(w io.Writer) {
	w.Write([]byte(strconv.FormatFloat(float64(*f), 'f', 64)))
}

type Integer int32

func (i *Integer) Decode(params map[string]string, data string) error {
	ci, err := strconv.ParseInt(data, 10, 32)
	if err != nil {
		return err
	}
	*i = Integer(ci)
	return nil
}

func (i *Integer) Encode(w io.Writer) {
	w.Write([]byte(strconv.FormatInt(int64(*i), 10)))
}

type Period struct {
	Start, End DateTime
	Duration   Duration
}

func (p *Period) Decode(params map[string]string, data string) error {
	i := strings.IndexByte(data, '/')
	if i == -1 || len(data) == i+1 {
		return ErrInvalidPeriod
	}
	err := p.Start.Decode(params, data[:i])
	if err != nil {
		return err
	}
	if data[i+1] == 'P' || data[i+1] == '+' {
		return p.Duration.Decode(params, data[i+1:])
	}
	return p.End.Decode(params, data[i+1:])
}

func (p *Period) Encode(w io.Writer) {
	p.Start.Encode(w)
	w.Write([]byte{'/'})
	if p.End.IsZero() {
		p.Duration.Encode(w)
	} else {
		p.End.Encode(w)
	}
}

type Recur struct {
}

func (r *Recur) Decode(params map[string]string, data string) error {
	return nil
}

func (r *Recur) Encode(w io.Writer) {

}

type Text string

func (t *Text) Decode(params map[string]string, data string) error {
	return nil
}

func (t *Text) Encode(w io.Writer) {

}

type Time struct {
	time.Time
}

func (t *Time) Decode(params map[string]string, data string) error {
	return nil
}

func (t *Time) Encode(w io.Writer) {

}

type URI struct {
	url.URL
}

func (u *URI) Decode(params map[string]string, data string) error {
	cu, err := url.Parse(data)
	if err != nil {
		return err
	}
	u.URL = cu
	return nil
}

func (u *URI) Encode(w io.Writer) {
	w.Write([]byte(u.URL.String()))
}

type UTCOffset int

func (u *UTCOffset) Decode(params map[string]string, data string) error {
	return nil
}

func (u *UTCOffset) Encode(w io.Writer) {

}

// Errors
var (
	ErrInvalidEncoding = errors.New("invalid binary encoding")
	ErrInvalidPeriod   = errors.New("invalid period")
	ErrInvalidDuration = errors.New("invalid duration")
)
<|endoftext|>


<commit_before>package ole

import (
	"syscall"
	"unsafe"
)

type IConnectionPoint struct {
	lpVtbl *pIConnectionPointVtbl
}

type pIConnectionPointVtbl struct {
	pQueryInterface              uintptr
	pAddRef                      uintptr
	pRelease                     uintptr
	pGetConnectionInterface      uintptr
	pGetConnectionPointContainer uintptr
	pAdvise                      uintptr
	pUnadvise                    uintptr
	pEnumConnections             uintptr
}

func (v *IConnectionPoint) QueryInterface(iid *GUID) (disp *IDispatch, err error) {
	disp, err = queryInterface((*IUnknown)(unsafe.Pointer(v)), iid)
	return
}

func (v *IConnectionPoint) AddRef() int32 {
	return addRef((*IUnknown)(unsafe.Pointer(v)))
}

func (v *IConnectionPoint) Release() int32 {
	return release((*IUnknown)(unsafe.Pointer(v)))
}

func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 {
	return release((*IUnknown)(unsafe.Pointer(v)))
}

func (v *IConnectionPoint) Advise(unknown *IUnknown) (cookie uint32, err error) {
	hr, _, _ := syscall.Syscall(
		uintptr(v.lpVtbl.pAdvise),
		3,
		uintptr(unsafe.Pointer(v)),
		uintptr(unsafe.Pointer(unknown)),
		uintptr(unsafe.Pointer(&cookie)))
	if hr != 0 {
		err = NewError(hr)
	}
	return
}

func (v *IConnectionPoint) Unadvise(cookie uint32) (err error) {
	hr, _, _ := syscall.Syscall(
		uintptr(v.lpVtbl.pAdvise),
		2,
		uintptr(unsafe.Pointer(v)),
		uintptr(cookie),
		0)
	if hr != 0 {
		err = NewError(hr)
	}
	return
}

func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) (err error) {
	return NewError(E_NOTIMPL)
}
<commit_msg>Fixes #28 (use correct vtable entry when calling Unadvise)<commit_after>package ole

import (
	"syscall"
	"unsafe"
)

type IConnectionPoint struct {
	lpVtbl *pIConnectionPointVtbl
}

type pIConnectionPointVtbl struct {
	pQueryInterface              uintptr
	pAddRef                      uintptr
	pRelease                     uintptr
	pGetConnectionInterface      uintptr
	pGetConnectionPointContainer uintptr
	pAdvise                      uintptr
	pUnadvise                    uintptr
	pEnumConnections             uintptr
}

func (v *IConnectionPoint) QueryInterface(iid *GUID) (disp *IDispatch, err error) {
	disp, err = queryInterface((*IUnknown)(unsafe.Pointer(v)), iid)
	return
}

func (v *IConnectionPoint) AddRef() int32 {
	return addRef((*IUnknown)(unsafe.Pointer(v)))
}

func (v *IConnectionPoint) Release() int32 {
	return release((*IUnknown)(unsafe.Pointer(v)))
}

func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 {
	return release((*IUnknown)(unsafe.Pointer(v)))
}

func (v *IConnectionPoint) Advise(unknown *IUnknown) (cookie uint32, err error) {
	hr, _, _ := syscall.Syscall(
		uintptr(v.lpVtbl.pAdvise),
		3,
		uintptr(unsafe.Pointer(v)),
		uintptr(unsafe.Pointer(unknown)),
		uintptr(unsafe.Pointer(&cookie)))
	if hr != 0 {
		err = NewError(hr)
	}
	return
}

func (v *IConnectionPoint) Unadvise(cookie uint32) (err error) {
	hr, _, _ := syscall.Syscall(
		uintptr(v.lpVtbl.pUnadvise),
		2,
		uintptr(unsafe.Pointer(v)),
		uintptr(cookie),
		0)
	if hr != 0 {
		err = NewError(hr)
	}
	return
}

func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) (err error) {
	return NewError(E_NOTIMPL)
}
<|endoftext|>


<commit_before>package mesos

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strconv"
	"strings"
	"sync"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/inputs"
	jsonparser "github.com/influxdata/telegraf/plugins/parsers/json"
)

type Mesos struct {
	Timeout    int
	Masters    []string
	MasterCols []string `toml:"metrics_collection"`
}

var defaultMetrics = []string{
	"resources", "master", "system", "slaves", "frameworks",
	"tasks", "messages", "evqueue", "messages", "registrar",
}

var sampleConfig = `
  # Timeout, in ms.
  timeout = 100
  # A list of Mesos masters. e.g. master1:5050, master2:5080, etc.
  # The port can be skipped if using the default (5050)
  # Default value is localhost:5050.
  masters = ["localhost:5050"]
  # Metrics groups to be collected.
  # Default, all enabled.
  master_collections = ["resources","master","system","slaves","frameworks","messages","evqueue","registrar"]
`

// SampleConfig returns a sample configuration block
func (m *Mesos) SampleConfig() string {
	return sampleConfig
}

// Description just returns a short description of the Mesos plugin
func (m *Mesos) Description() string {
	return "Telegraf plugin for gathering metrics from N Mesos masters"
}

// Gather() metrics from given list of Mesos Masters
func (m *Mesos) Gather(acc telegraf.Accumulator) error {
	var wg sync.WaitGroup
	var errorChannel chan error

	if len(m.Masters) == 0 {
		m.Masters = []string{"localhost:5050"}
	}

	errorChannel = make(chan error, len(m.Masters)*2)

	for _, v := range m.Masters {
		wg.Add(1)
		go func() {
			errorChannel <- m.gatherMetrics(v, acc)
			wg.Done()
			return
		}()
	}

	wg.Wait()
	close(errorChannel)
	errorStrings := []string{}

	// Gather all errors for returning them at once
	for err := range errorChannel {
		if err != nil {
			errorStrings = append(errorStrings, err.Error())
		}
	}

	if len(errorStrings) > 0 {
		return errors.New(strings.Join(errorStrings, "\n"))
	}
	return nil
}

// metricsDiff() returns set names for removal
func metricsDiff(w []string) []string {
	b := []string{}
	s := make(map[string]bool)

	if len(w) == 0 {
		return b
	}

	for _, v := range w {
		s[v] = true
	}

	for _, d := range defaultMetrics {
		if _, ok := s[d]; !ok {
			b = append(b, d)
		}
	}

	return b
}

// masterBlocks serves as kind of metrics registry groupping them in sets
func masterBlocks(g string) []string {
	var m map[string][]string

	m = make(map[string][]string)

	m["resources"] = []string{
		"master/cpus_percent",
		"master/cpus_used",
		"master/cpus_total",
		"master/cpus_revocable_percent",
		"master/cpus_revocable_total",
		"master/cpus_revocable_used",
		"master/disk_percent",
		"master/disk_used",
		"master/disk_total",
		"master/disk_revocable_percent",
		"master/disk_revocable_total",
		"master/disk_revocable_used",
		"master/mem_percent",
		"master/mem_used",
		"master/mem_total",
		"master/mem_revocable_percent",
		"master/mem_revocable_total",
		"master/mem_revocable_used",
	}

	m["master"] = []string{
		"master/elected",
		"master/uptime_secs",
	}

	m["system"] = []string{
		"system/cpus_total",
		"system/load_15min",
		"system/load_5min",
		"system/load_1min",
		"system/mem_free_bytes",
		"system/mem_total_bytes",
	}

	m["slaves"] = []string{
		"master/slave_registrations",
		"master/slave_removals",
		"master/slave_reregistrations",
		"master/slave_shutdowns_scheduled",
		"master/slave_shutdowns_canceled",
		"master/slave_shutdowns_completed",
		"master/slaves_active",
		"master/slaves_connected",
		"master/slaves_disconnected",
		"master/slaves_inactive",
	}

	m["frameworks"] = []string{
		"master/frameworks_active",
		"master/frameworks_connected",
		"master/frameworks_disconnected",
		"master/frameworks_inactive",
		"master/outstanding_offers",
	}

	m["tasks"] = []string{
		"master/tasks_error",
		"master/tasks_failed",
		"master/tasks_finished",
		"master/tasks_killed",
		"master/tasks_lost",
		"master/tasks_running",
		"master/tasks_staging",
		"master/tasks_starting",
	}

	m["messages"] = []string{
		"master/invalid_executor_to_framework_messages",
		"master/invalid_framework_to_executor_messages",
		"master/invalid_status_update_acknowledgements",
		"master/invalid_status_updates",
		"master/dropped_messages",
		"master/messages_authenticate",
		"master/messages_deactivate_framework",
		"master/messages_decline_offers",
		"master/messages_executor_to_framework",
		"master/messages_exited_executor",
		"master/messages_framework_to_executor",
		"master/messages_kill_task",
		"master/messages_launch_tasks",
		"master/messages_reconcile_tasks",
		"master/messages_register_framework",
		"master/messages_register_slave",
		"master/messages_reregister_framework",
		"master/messages_reregister_slave",
		"master/messages_resource_request",
		"master/messages_revive_offers",
		"master/messages_status_update",
		"master/messages_status_update_acknowledgement",
		"master/messages_unregister_framework",
		"master/messages_unregister_slave",
		"master/messages_update_slave",
		"master/recovery_slave_removals",
		"master/slave_removals/reason_registered",
		"master/slave_removals/reason_unhealthy",
		"master/slave_removals/reason_unregistered",
		"master/valid_framework_to_executor_messages",
		"master/valid_status_update_acknowledgements",
		"master/valid_status_updates",
		"master/task_lost/source_master/reason_invalid_offers",
		"master/task_lost/source_master/reason_slave_removed",
		"master/task_lost/source_slave/reason_executor_terminated",
		"master/valid_executor_to_framework_messages",
	}

	m["evqueue"] = []string{
		"master/event_queue_dispatches",
		"master/event_queue_http_requests",
		"master/event_queue_messages",
	}

	m["registrar"] = []string{
		"registrar/state_fetch_ms",
		"registrar/state_store_ms",
		"registrar/state_store_ms/max",
		"registrar/state_store_ms/min",
		"registrar/state_store_ms/p50",
		"registrar/state_store_ms/p90",
		"registrar/state_store_ms/p95",
		"registrar/state_store_ms/p99",
		"registrar/state_store_ms/p999",
		"registrar/state_store_ms/p9999",
	}

	ret, ok := m[g]

	if !ok {
		log.Println("[mesos] Unkown metrics group: ", g)
		return []string{}
	}

	return ret
}

// removeGroup(), remove unwanted sets
func (m *Mesos) removeGroup(j *map[string]interface{}) {
	var ok bool

	b := metricsDiff(m.MasterCols)

	for _, k := range b {
		for _, v := range masterBlocks(k) {
			if _, ok = (*j)[v]; ok {
				delete((*j), v)
			}
		}
	}
}

// This should not belong to the object
func (m *Mesos) gatherMetrics(a string, acc telegraf.Accumulator) error {
	var jsonOut map[string]interface{}

	host, _, err := net.SplitHostPort(a)
	if err != nil {
		host = a
		a = a + ":5050"
	}

	tags := map[string]string{
		"server": host,
	}

	if m.Timeout == 0 {
		log.Println("[mesos] Missing timeout value, setting default value (100ms)")
		m.Timeout = 100
	}

	ts := strconv.Itoa(m.Timeout) + "ms"

	resp, err := http.Get("http://" + a + "/metrics/snapshot?timeout=" + ts)

	if err != nil {
		return err
	}

	data, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return err
	}

	if err = json.Unmarshal([]byte(data), &jsonOut); err != nil {
		return errors.New("Error decoding JSON response")
	}

	m.removeGroup(&jsonOut)

	jf := jsonparser.JSONFlattener{}

	err = jf.FlattenJSON("", jsonOut)

	if err != nil {
		return err
	}

	acc.AddFields("mesos", jf.Fields, tags)

	return nil
}

func init() {
	inputs.Add("mesos", func() telegraf.Input {
		return &Mesos{}
	})
}
<commit_msg>fix(vet): Range var used by goroutine<commit_after>package mesos

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strconv"
	"strings"
	"sync"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/inputs"
	jsonparser "github.com/influxdata/telegraf/plugins/parsers/json"
)

type Mesos struct {
	Timeout    int
	Masters    []string
	MasterCols []string `toml:"metrics_collection"`
}

var defaultMetrics = []string{
	"resources", "master", "system", "slaves", "frameworks",
	"tasks", "messages", "evqueue", "messages", "registrar",
}

var sampleConfig = `
  # Timeout, in ms.
  timeout = 100
  # A list of Mesos masters. e.g. master1:5050, master2:5080, etc.
  # The port can be skipped if using the default (5050)
  # Default value is localhost:5050.
  masters = ["localhost:5050"]
  # Metrics groups to be collected.
  # Default, all enabled.
  master_collections = ["resources","master","system","slaves","frameworks","messages","evqueue","registrar"]
`

// SampleConfig returns a sample configuration block
func (m *Mesos) SampleConfig() string {
	return sampleConfig
}

// Description just returns a short description of the Mesos plugin
func (m *Mesos) Description() string {
	return "Telegraf plugin for gathering metrics from N Mesos masters"
}

// Gather() metrics from given list of Mesos Masters
func (m *Mesos) Gather(acc telegraf.Accumulator) error {
	var wg sync.WaitGroup
	var errorChannel chan error

	if len(m.Masters) == 0 {
		m.Masters = []string{"localhost:5050"}
	}

	errorChannel = make(chan error, len(m.Masters)*2)

	for _, v := range m.Masters {
		wg.Add(1)
		go func(c string) {
			errorChannel <- m.gatherMetrics(c, acc)
			wg.Done()
			return
		}(v)
	}

	wg.Wait()
	close(errorChannel)
	errorStrings := []string{}

	// Gather all errors for returning them at once
	for err := range errorChannel {
		if err != nil {
			errorStrings = append(errorStrings, err.Error())
		}
	}

	if len(errorStrings) > 0 {
		return errors.New(strings.Join(errorStrings, "\n"))
	}
	return nil
}

// metricsDiff() returns set names for removal
func metricsDiff(w []string) []string {
	b := []string{}
	s := make(map[string]bool)

	if len(w) == 0 {
		return b
	}

	for _, v := range w {
		s[v] = true
	}

	for _, d := range defaultMetrics {
		if _, ok := s[d]; !ok {
			b = append(b, d)
		}
	}

	return b
}

// masterBlocks serves as kind of metrics registry groupping them in sets
func masterBlocks(g string) []string {
	var m map[string][]string

	m = make(map[string][]string)

	m["resources"] = []string{
		"master/cpus_percent",
		"master/cpus_used",
		"master/cpus_total",
		"master/cpus_revocable_percent",
		"master/cpus_revocable_total",
		"master/cpus_revocable_used",
		"master/disk_percent",
		"master/disk_used",
		"master/disk_total",
		"master/disk_revocable_percent",
		"master/disk_revocable_total",
		"master/disk_revocable_used",
		"master/mem_percent",
		"master/mem_used",
		"master/mem_total",
		"master/mem_revocable_percent",
		"master/mem_revocable_total",
		"master/mem_revocable_used",
	}

	m["master"] = []string{
		"master/elected",
		"master/uptime_secs",
	}

	m["system"] = []string{
		"system/cpus_total",
		"system/load_15min",
		"system/load_5min",
		"system/load_1min",
		"system/mem_free_bytes",
		"system/mem_total_bytes",
	}

	m["slaves"] = []string{
		"master/slave_registrations",
		"master/slave_removals",
		"master/slave_reregistrations",
		"master/slave_shutdowns_scheduled",
		"master/slave_shutdowns_canceled",
		"master/slave_shutdowns_completed",
		"master/slaves_active",
		"master/slaves_connected",
		"master/slaves_disconnected",
		"master/slaves_inactive",
	}

	m["frameworks"] = []string{
		"master/frameworks_active",
		"master/frameworks_connected",
		"master/frameworks_disconnected",
		"master/frameworks_inactive",
		"master/outstanding_offers",
	}

	m["tasks"] = []string{
		"master/tasks_error",
		"master/tasks_failed",
		"master/tasks_finished",
		"master/tasks_killed",
		"master/tasks_lost",
		"master/tasks_running",
		"master/tasks_staging",
		"master/tasks_starting",
	}

	m["messages"] = []string{
		"master/invalid_executor_to_framework_messages",
		"master/invalid_framework_to_executor_messages",
		"master/invalid_status_update_acknowledgements",
		"master/invalid_status_updates",
		"master/dropped_messages",
		"master/messages_authenticate",
		"master/messages_deactivate_framework",
		"master/messages_decline_offers",
		"master/messages_executor_to_framework",
		"master/messages_exited_executor",
		"master/messages_framework_to_executor",
		"master/messages_kill_task",
		"master/messages_launch_tasks",
		"master/messages_reconcile_tasks",
		"master/messages_register_framework",
		"master/messages_register_slave",
		"master/messages_reregister_framework",
		"master/messages_reregister_slave",
		"master/messages_resource_request",
		"master/messages_revive_offers",
		"master/messages_status_update",
		"master/messages_status_update_acknowledgement",
		"master/messages_unregister_framework",
		"master/messages_unregister_slave",
		"master/messages_update_slave",
		"master/recovery_slave_removals",
		"master/slave_removals/reason_registered",
		"master/slave_removals/reason_unhealthy",
		"master/slave_removals/reason_unregistered",
		"master/valid_framework_to_executor_messages",
		"master/valid_status_update_acknowledgements",
		"master/valid_status_updates",
		"master/task_lost/source_master/reason_invalid_offers",
		"master/task_lost/source_master/reason_slave_removed",
		"master/task_lost/source_slave/reason_executor_terminated",
		"master/valid_executor_to_framework_messages",
	}

	m["evqueue"] = []string{
		"master/event_queue_dispatches",
		"master/event_queue_http_requests",
		"master/event_queue_messages",
	}

	m["registrar"] = []string{
		"registrar/state_fetch_ms",
		"registrar/state_store_ms",
		"registrar/state_store_ms/max",
		"registrar/state_store_ms/min",
		"registrar/state_store_ms/p50",
		"registrar/state_store_ms/p90",
		"registrar/state_store_ms/p95",
		"registrar/state_store_ms/p99",
		"registrar/state_store_ms/p999",
		"registrar/state_store_ms/p9999",
	}

	ret, ok := m[g]

	if !ok {
		log.Println("[mesos] Unkown metrics group: ", g)
		return []string{}
	}

	return ret
}

// removeGroup(), remove unwanted sets
func (m *Mesos) removeGroup(j *map[string]interface{}) {
	var ok bool

	b := metricsDiff(m.MasterCols)

	for _, k := range b {
		for _, v := range masterBlocks(k) {
			if _, ok = (*j)[v]; ok {
				delete((*j), v)
			}
		}
	}
}

// This should not belong to the object
func (m *Mesos) gatherMetrics(a string, acc telegraf.Accumulator) error {
	var jsonOut map[string]interface{}

	host, _, err := net.SplitHostPort(a)
	if err != nil {
		host = a
		a = a + ":5050"
	}

	tags := map[string]string{
		"server": host,
	}

	if m.Timeout == 0 {
		log.Println("[mesos] Missing timeout value, setting default value (100ms)")
		m.Timeout = 100
	}

	ts := strconv.Itoa(m.Timeout) + "ms"

	resp, err := http.Get("http://" + a + "/metrics/snapshot?timeout=" + ts)

	if err != nil {
		return err
	}

	data, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return err
	}

	if err = json.Unmarshal([]byte(data), &jsonOut); err != nil {
		return errors.New("Error decoding JSON response")
	}

	m.removeGroup(&jsonOut)

	jf := jsonparser.JSONFlattener{}

	err = jf.FlattenJSON("", jsonOut)

	if err != nil {
		return err
	}

	acc.AddFields("mesos", jf.Fields, tags)

	return nil
}

func init() {
	inputs.Add("mesos", func() telegraf.Input {
		return &Mesos{}
	})
}
<|endoftext|>


<commit_before>package ui

import (
	"sync"

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

// Labels is a map of each allowed key press (rune) to it's full string label.
var Labels = map[rune]string{
	'a': "Visual imagination",
	'd': "Language thought",
	'e': "Language voice",
	'q': "Visual memory",
	's': "Auditory imagination",
	'w': "Auditory memory",
}

const title = `           _ _
  _ __  __| | |_
 | '  \/ _  |  _|
 |_|_|_\__,_|\__|

`

var (
	mu        sync.Mutex
	inputs    []*Input
	keys      []*KeyLabel
	statusBar *StatusBar
	config    Config
)

const (
	inputLabelWidth      = 10
	inputWidth           = 10
	inputMinutesBufWidth = 3
	inputHzBufWidth      = 5
	keyLabelWidth        = 3
	keyWidth             = 21
	statusBarWidth       = 60
	statusBarDefaultText = "Press 'space' to start capturing keys, 'Esc' to quit."
)

// Init must be called before any other function. It initializes
// configuration and termbox.
func Init() error {
	if err := initConfig(); err != nil {
		return err
	}
	if err := initTermbox(); err != nil {
		return err
	}
	return nil
}

// Loading configuration from config.json
func initConfig() error {
	config = Config{}
	err := config.Load()
	if err != nil {
		return err
	}
	return nil
}

// Initializing termbox
func initTermbox() error {
	err := termbox.Init()
	if err != nil {
		return err
	}
	termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse)
	termbox.SetOutputMode(termbox.OutputNormal)
	termbox.Clear(termbox.ColorWhite, termbox.ColorDefault)
	return nil
}

// Close should be deferred after initialization. It finalizes termbox library.
func Close() {
	mu.Lock()
	termbox.Close()
	mu.Unlock()
}

// DrawAll draws the title, the inputs, the key labels and the status bar.
// It should also be called when a resize event is received.
func DrawAll() {
	inputs = nil
	keys = nil
	statusBar = nil
	_, keysY := drawTitle(0, 0)
	keysX, sbY := drawInputs(0, keysY+1)
	_, _ = drawKeys(keysX+3, keysY+1)
	_, _ = drawStatusBar(0, sbY+1)
}

func drawTitle(x, y int) (maxX, maxY int) {
	return text(x, y, title)
}

func drawStatusBar(x, y int) (maxX, maxY int) {
	sbw := statusBarWidth
	statusBar = NewStatusBar(x, y, sbw, statusBarDefaultText)
	statusBar.Draw()
	return statusBar.MaxX(), statusBar.MaxY()
}

func drawInputs(x, y int) (maxX, maxY int) {
	const lw = inputLabelWidth
	const w = inputWidth
	in1 := NewInput(x, y+0, lw, "Mode", w, 0, config.ModeS(), false, InputSwitch, configMode)
	in2 := NewInput(x, y+2, lw, "TotalTime", w, inputMinutesBufWidth, config.TotalTimeS(), true, InputNumericInt, configTotalTime)
	in3 := NewInput(x, y+4, lw, "Offset", w, inputMinutesBufWidth, config.OffsetS(), true, InputNumericInt, configOffset)
	in4 := NewInput(x, y+6, lw, "BaseHz", w, inputHzBufWidth, config.BaseHzS(), true, InputNumericFloat, configBaseHz)
	in5 := NewInput(x, y+8, lw, "StartHz", w, inputHzBufWidth, config.StartHzS(), true, InputNumericFloat, configStartHz)
	in6 := NewInput(x, y+10, lw, "EndHz", w, inputHzBufWidth, config.EndHzS(), true, InputNumericFloat, configEndHz)
	inputs = nil
	inputs = append(inputs, in1, in2, in3, in4, in5, in6)
	for _, in := range inputs {
		in.Draw()
	}
	return in6.MaxX(), in6.MaxY()
}

// ReloadInputs updates each input with the values of a new configuration.
// It should be called after receiving a valid value from en enabled input.
func ReloadInputs(c Config) {
	inputs[0].T = c.ModeS()
	inputs[1].T = c.TotalTimeS()
	inputs[2].T = c.OffsetS()
	inputs[3].T = c.BaseHzS()
	inputs[4].T = c.StartHzS()
	inputs[5].T = c.EndHzS()
	for _, in := range inputs {
		in.ClearBuf()
		in.ResetText()
	}
}

func drawKeys(x, y int) (maxX, maxY int) {
	const lw = keyLabelWidth
	const w = keyWidth
	k1 := NewKeyLabel(x, y+0, lw, rtoa('q'), w, Labels['q'], false)
	k2 := NewKeyLabel(x, y+2, lw, rtoa('a'), w, Labels['a'], true)
	k3 := NewKeyLabel(x, y+4, lw, rtoa('w'), w, Labels['w'], true)
	k4 := NewKeyLabel(x, y+6, lw, rtoa('s'), w, Labels['s'], true)
	k5 := NewKeyLabel(x, y+8, lw, rtoa('e'), w, Labels['e'], true)
	k6 := NewKeyLabel(x, y+10, lw, rtoa('d'), w, Labels['d'], true)
	keys = append(keys, k1, k2, k3, k4, k5, k6)
	for _, k := range keys {
		k.Draw()
	}
	return k6.MaxX(), k6.MaxY()
}

// Cell wraps a termbox cell. A single conceptual entity on the screen. A
// screen considered a 2d array of cells. Each cell also holds a reference
// to an input if it exists, thus by clicking a cell that contains an input
// we can easily access it.
type Cell struct {
	Input *Input
	termbox.Cell
}

func cells() [][]Cell {
	mx, my := termbox.Size()
	cellBuffer := termbox.CellBuffer()
	cells := make([][]Cell, mx)
	for k := range cells {
		cells[k] = make([]Cell, my)
	}
	i, j := 0, 0
	for _, c := range cellBuffer {
		if i == mx {
			j++
			i = 0
		}
		cells[i][j].Ch = c.Ch
		cells[i][j].Fg = c.Fg
		cells[i][j].Bg = c.Bg
		i++
	}
	return cells
}

func registerInputs(cells [][]Cell) [][]Cell {
	for _, in := range inputs {
		for x := in.TextStartX(); x <= in.TextEndX(); x++ {
			cells[x][in.TextY()-1].Input = in
			cells[x][in.TextY()+0].Input = in
			cells[x][in.TextY()+1].Input = in
		}
	}
	return cells
}

// Cells initializes all cells on screen and registers the inputs on the cells
// that should have them.
func Cells() [][]Cell {
	c := cells()
	return registerInputs(c)
}

// GetCell returns a cell based on the x, y points of the screen.
func GetCell(x, y int) Cell {
	c := Cells()
	return c[x][y]
}

// Scan scans the cells of the screen that are contained at a certain y,
// between startX and endX. It returns the content of the scanned cells as
// a string.
func Scan(startX, endX, y int) string {
	c := Cells()
	var runes []rune
	for x := startX; x <= endX; x++ {
		runes = append(runes, c[x][y].Ch)
	}
	return string(runes)
}

// KeyLabel holds a label with each allowed key press and right next, the
// coresponding text that describes the label.
type KeyLabel struct {
	X      int
	Y      int
	LabelW int    // label width
	LabelT string // label text
	W      int    // rest of width
	T      string // text describing the key
	a      bool
	S      bool
}

// NewKeyLabel creates a new KeyLabel.
func NewKeyLabel(x, y, labelW int, labelT string, w int, t string, a bool) *KeyLabel {
	return &KeyLabel{x, y, labelW, labelT, w, t, a, false}
}

// MaxX returns the maximum x that a KeyLabel reaches on the screen.
func (kl KeyLabel) MaxX() int {
	return kl.X + kl.LabelW + 3 + kl.W
}

// MaxY returns the maximum y that a KeyLabel reaches on the screen.
func (kl KeyLabel) MaxY() int {
	return kl.Y + 2
}

// Draw draws the KeyLabel.
func (kl KeyLabel) Draw() {
	x := kl.X
	y := kl.Y
	lw := kl.LabelW
	lt := kl.LabelT
	w := kl.W
	t := kl.T

	fill(x, y+0, 1, 1, '┌')
	fill(x, y+1, 1, 1, '│')
	fill(x, y+2, 1, 1, '└')
	fill(x+1, y+0, lw, 1, '─')

	text(x+1, y+1, lt)
	fill(x+1, y+2, lw, 1, '─')
	fill(x+lw+1, y+0, 1, 1, '─')
	fill(x+lw+1, y+1, 1, 1, ' ')
	fill(x+lw+1, y+2, 1, 1, '─')
	fill(x+lw+2, y+0, w, 1, '─')
	text(x+lw+2, y+1, t)
	fill(x+lw+2, y+2, w, 1, '─')
	fill(x+lw+2+w, y+0, 1, 1, '┐')
	fill(x+lw+2+w, y+1, 1, 1, '│')
	fill(x+lw+2+w, y+2, 1, 1, '┘')
	if kl.a {
		fill(x, y+0, 1, 1, '├')
		fill(x+lw+2+w, y+0, 1, 1, '┤')
	}
}

// StatusBar holds the data for drawing a status bar with a specified width
// and text. It also has space to the left for the timer.
type StatusBar struct {
	X          int
	Y          int
	Width      int
	Text       string
	timerWidth int
}

// NewStatusBar returns a new StatusBar.
func NewStatusBar(x, y, width int, text string) *StatusBar {
	return &StatusBar{x, y, width, text, 6}
}

// MaxX returns the maximum x that a StatusBar can reach on the screen.
func (sb StatusBar) MaxX() int {
	return sb.X + sb.timerWidth + 2 + sb.Width
}

// MaxY returns the maximum y that a StatusBar can reach on the screen.
func (sb StatusBar) MaxY() int {
	return sb.Y + 2
}

// Draw draws the StatusBar.
func (sb StatusBar) Draw() {
	x := sb.X
	y := sb.Y
	w := sb.Width
	t := sb.Text
	tw := sb.timerWidth

	// unicode box drawing chars around the edit box
	fill(x, y+0, 1, 1, '╔')
	fill(x, y+1, 1, 1, '║')
	fill(x, y+2, 1, 1, '╚')
	fill(x+1, y+0, tw, 1, '═')
	fill(x+1, y+2, tw, 1, '═')
	fill(x+tw+1, y+0, 1, 1, '╤')
	fill(x+tw+1, y+1, 1, 1, '│')
	fill(x+tw+1, y+2, 1, 1, '╧')
	fill(x+tw+2, y+0, w, 1, '═')
	fill(x+tw+2, y+2, w, 1, '═')
	fill(x+tw+2+w, y+0, 1, 1, '╗')
	fill(x+tw+2+w, y+1, 1, 1, '║')
	fill(x+tw+2+w, y+2, 1, 1, '╝')
	text(x+tw+2, y+1, t)

	flush()
}

// UpdateTimer updates the timer of the status bar by a specified amount of
// seconds.
func (sb StatusBar) UpdateTimer(seconds int) {
	text(sb.X+1, sb.Y+1, FormatTimer(seconds))
	flush()
}

// UpdateText updates the text of the status bar.
func (sb StatusBar) UpdateText(t string) {
	//Text(sb.X+sb.timerWidth+2, sb.Y+1, text[0:sb.Width]) // text[0:sb.Width] panics for some reason
	fill(sb.X+sb.timerWidth+2, sb.Y+1, sb.Width, 1, ' ')
	text(sb.X+sb.timerWidth+2, sb.Y+1, t)
	flush()
}

// UpdateTimer is a helper function that updates the status bar's timer.
func UpdateTimer(seconds int) {
	if statusBar != nil {
		statusBar.UpdateTimer(seconds)
	}
}

// ResetTimer clears the status bar's timer.
func ResetTimer() {
	if statusBar != nil {
		fill(statusBar.X+1, statusBar.Y+1, statusBar.timerWidth, 1, ' ')
		flush()
	}
}

// UpdateText is a helper function that updates the status bar's text.
func UpdateText(text string) {
	if statusBar != nil {
		statusBar.UpdateText(text)
	}
}

// ResetText clears the status bar's text.
func ResetText() {
	if statusBar != nil {
		statusBar.UpdateText(statusBarDefaultText)
	}
}

// Capture represents a captured key press at a specific second of the timer
// along with the value of Hz that was recorded.
type Capture struct {
	Value   rune
	Seconds int
	Hz      float64
}

// Label returns the description of the captured key value.
func (c *Capture) Label() string {
	return Labels[c.Value]
}

// Timestamp returns the timestamp of when a capture happened.
func (c *Capture) Timestamp() string {
	return FormatTimer(c.Seconds)
}
<commit_msg>Add version next to the title<commit_after>package ui

import (
	"sync"

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

// Labels is a map of each allowed key press (rune) to it's full string label.
var Labels = map[rune]string{
	'a': "Visual imagination",
	'd': "Language thought",
	'e': "Language voice",
	'q': "Visual memory",
	's': "Auditory imagination",
	'w': "Auditory memory",
}

const title = `           _ _
  _ __  __| | |_
 | '  \/ _  |  _|
 |_|_|_\__,_|\__|

`

var (
	Version   = "devel"
	mu        sync.Mutex
	inputs    []*Input
	keys      []*KeyLabel
	statusBar *StatusBar
	config    Config
)

const (
	inputLabelWidth      = 10
	inputWidth           = 10
	inputMinutesBufWidth = 3
	inputHzBufWidth      = 5
	keyLabelWidth        = 3
	keyWidth             = 21
	statusBarWidth       = 60
	statusBarDefaultText = "Press 'space' to start capturing keys, 'Esc' to quit."
)

// Init must be called before any other function. It initializes
// configuration and termbox.
func Init() error {
	if err := initConfig(); err != nil {
		return err
	}
	if err := initTermbox(); err != nil {
		return err
	}
	return nil
}

// Loading configuration from config.json
func initConfig() error {
	config = Config{}
	err := config.Load()
	if err != nil {
		return err
	}
	return nil
}

// Initializing termbox
func initTermbox() error {
	err := termbox.Init()
	if err != nil {
		return err
	}
	termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse)
	termbox.SetOutputMode(termbox.OutputNormal)
	termbox.Clear(termbox.ColorWhite, termbox.ColorDefault)
	return nil
}

// Close should be deferred after initialization. It finalizes termbox library.
func Close() {
	mu.Lock()
	termbox.Close()
	mu.Unlock()
}

// DrawAll draws the title, the inputs, the key labels and the status bar.
// It should also be called when a resize event is received.
func DrawAll() {
	inputs = nil
	keys = nil
	statusBar = nil
	_, keysY := drawTitle(0, 0, Version)
	keysX, sbY := drawInputs(0, keysY+1)
	_, _ = drawKeys(keysX+3, keysY+1)
	_, _ = drawStatusBar(0, sbY+1)
}

func drawTitle(x, y int, version string) (maxX, maxY int) {
	titleX, titleY := text(x, y, title)
	if version != "devel" {
		version = "v" + version
	}
	// draw version
	text(titleX+1, titleY-2, version)
	return titleX, titleY
}

func drawStatusBar(x, y int) (maxX, maxY int) {
	sbw := statusBarWidth
	statusBar = NewStatusBar(x, y, sbw, statusBarDefaultText)
	statusBar.Draw()
	return statusBar.MaxX(), statusBar.MaxY()
}

func drawInputs(x, y int) (maxX, maxY int) {
	const lw = inputLabelWidth
	const w = inputWidth
	in1 := NewInput(x, y+0, lw, "Mode", w, 0, config.ModeS(), false, InputSwitch, configMode)
	in2 := NewInput(x, y+2, lw, "TotalTime", w, inputMinutesBufWidth, config.TotalTimeS(), true, InputNumericInt, configTotalTime)
	in3 := NewInput(x, y+4, lw, "Offset", w, inputMinutesBufWidth, config.OffsetS(), true, InputNumericInt, configOffset)
	in4 := NewInput(x, y+6, lw, "BaseHz", w, inputHzBufWidth, config.BaseHzS(), true, InputNumericFloat, configBaseHz)
	in5 := NewInput(x, y+8, lw, "StartHz", w, inputHzBufWidth, config.StartHzS(), true, InputNumericFloat, configStartHz)
	in6 := NewInput(x, y+10, lw, "EndHz", w, inputHzBufWidth, config.EndHzS(), true, InputNumericFloat, configEndHz)
	inputs = nil
	inputs = append(inputs, in1, in2, in3, in4, in5, in6)
	for _, in := range inputs {
		in.Draw()
	}
	return in6.MaxX(), in6.MaxY()
}

// ReloadInputs updates each input with the values of a new configuration.
// It should be called after receiving a valid value from en enabled input.
func ReloadInputs(c Config) {
	inputs[0].T = c.ModeS()
	inputs[1].T = c.TotalTimeS()
	inputs[2].T = c.OffsetS()
	inputs[3].T = c.BaseHzS()
	inputs[4].T = c.StartHzS()
	inputs[5].T = c.EndHzS()
	for _, in := range inputs {
		in.ClearBuf()
		in.ResetText()
	}
}

func drawKeys(x, y int) (maxX, maxY int) {
	const lw = keyLabelWidth
	const w = keyWidth
	k1 := NewKeyLabel(x, y+0, lw, rtoa('q'), w, Labels['q'], false)
	k2 := NewKeyLabel(x, y+2, lw, rtoa('a'), w, Labels['a'], true)
	k3 := NewKeyLabel(x, y+4, lw, rtoa('w'), w, Labels['w'], true)
	k4 := NewKeyLabel(x, y+6, lw, rtoa('s'), w, Labels['s'], true)
	k5 := NewKeyLabel(x, y+8, lw, rtoa('e'), w, Labels['e'], true)
	k6 := NewKeyLabel(x, y+10, lw, rtoa('d'), w, Labels['d'], true)
	keys = append(keys, k1, k2, k3, k4, k5, k6)
	for _, k := range keys {
		k.Draw()
	}
	return k6.MaxX(), k6.MaxY()
}

// Cell wraps a termbox cell. A single conceptual entity on the screen. A
// screen considered a 2d array of cells. Each cell also holds a reference
// to an input if it exists, thus by clicking a cell that contains an input
// we can easily access it.
type Cell struct {
	Input *Input
	termbox.Cell
}

func cells() [][]Cell {
	mx, my := termbox.Size()
	cellBuffer := termbox.CellBuffer()
	cells := make([][]Cell, mx)
	for k := range cells {
		cells[k] = make([]Cell, my)
	}
	i, j := 0, 0
	for _, c := range cellBuffer {
		if i == mx {
			j++
			i = 0
		}
		cells[i][j].Ch = c.Ch
		cells[i][j].Fg = c.Fg
		cells[i][j].Bg = c.Bg
		i++
	}
	return cells
}

func registerInputs(cells [][]Cell) [][]Cell {
	for _, in := range inputs {
		for x := in.TextStartX(); x <= in.TextEndX(); x++ {
			cells[x][in.TextY()-1].Input = in
			cells[x][in.TextY()+0].Input = in
			cells[x][in.TextY()+1].Input = in
		}
	}
	return cells
}

// Cells initializes all cells on screen and registers the inputs on the cells
// that should have them.
func Cells() [][]Cell {
	c := cells()
	return registerInputs(c)
}

// GetCell returns a cell based on the x, y points of the screen.
func GetCell(x, y int) Cell {
	c := Cells()
	return c[x][y]
}

// Scan scans the cells of the screen that are contained at a certain y,
// between startX and endX. It returns the content of the scanned cells as
// a string.
func Scan(startX, endX, y int) string {
	c := Cells()
	var runes []rune
	for x := startX; x <= endX; x++ {
		runes = append(runes, c[x][y].Ch)
	}
	return string(runes)
}

// KeyLabel holds a label with each allowed key press and right next, the
// coresponding text that describes the label.
type KeyLabel struct {
	X      int
	Y      int
	LabelW int    // label width
	LabelT string // label text
	W      int    // rest of width
	T      string // text describing the key
	a      bool
	S      bool
}

// NewKeyLabel creates a new KeyLabel.
func NewKeyLabel(x, y, labelW int, labelT string, w int, t string, a bool) *KeyLabel {
	return &KeyLabel{x, y, labelW, labelT, w, t, a, false}
}

// MaxX returns the maximum x that a KeyLabel reaches on the screen.
func (kl KeyLabel) MaxX() int {
	return kl.X + kl.LabelW + 3 + kl.W
}

// MaxY returns the maximum y that a KeyLabel reaches on the screen.
func (kl KeyLabel) MaxY() int {
	return kl.Y + 2
}

// Draw draws the KeyLabel.
func (kl KeyLabel) Draw() {
	x := kl.X
	y := kl.Y
	lw := kl.LabelW
	lt := kl.LabelT
	w := kl.W
	t := kl.T

	fill(x, y+0, 1, 1, '┌')
	fill(x, y+1, 1, 1, '│')
	fill(x, y+2, 1, 1, '└')
	fill(x+1, y+0, lw, 1, '─')

	text(x+1, y+1, lt)
	fill(x+1, y+2, lw, 1, '─')
	fill(x+lw+1, y+0, 1, 1, '─')
	fill(x+lw+1, y+1, 1, 1, ' ')
	fill(x+lw+1, y+2, 1, 1, '─')
	fill(x+lw+2, y+0, w, 1, '─')
	text(x+lw+2, y+1, t)
	fill(x+lw+2, y+2, w, 1, '─')
	fill(x+lw+2+w, y+0, 1, 1, '┐')
	fill(x+lw+2+w, y+1, 1, 1, '│')
	fill(x+lw+2+w, y+2, 1, 1, '┘')
	if kl.a {
		fill(x, y+0, 1, 1, '├')
		fill(x+lw+2+w, y+0, 1, 1, '┤')
	}
}

// StatusBar holds the data for drawing a status bar with a specified width
// and text. It also has space to the left for the timer.
type StatusBar struct {
	X          int
	Y          int
	Width      int
	Text       string
	timerWidth int
}

// NewStatusBar returns a new StatusBar.
func NewStatusBar(x, y, width int, text string) *StatusBar {
	return &StatusBar{x, y, width, text, 6}
}

// MaxX returns the maximum x that a StatusBar can reach on the screen.
func (sb StatusBar) MaxX() int {
	return sb.X + sb.timerWidth + 2 + sb.Width
}

// MaxY returns the maximum y that a StatusBar can reach on the screen.
func (sb StatusBar) MaxY() int {
	return sb.Y + 2
}

// Draw draws the StatusBar.
func (sb StatusBar) Draw() {
	x := sb.X
	y := sb.Y
	w := sb.Width
	t := sb.Text
	tw := sb.timerWidth

	// unicode box drawing chars around the edit box
	fill(x, y+0, 1, 1, '╔')
	fill(x, y+1, 1, 1, '║')
	fill(x, y+2, 1, 1, '╚')
	fill(x+1, y+0, tw, 1, '═')
	fill(x+1, y+2, tw, 1, '═')
	fill(x+tw+1, y+0, 1, 1, '╤')
	fill(x+tw+1, y+1, 1, 1, '│')
	fill(x+tw+1, y+2, 1, 1, '╧')
	fill(x+tw+2, y+0, w, 1, '═')
	fill(x+tw+2, y+2, w, 1, '═')
	fill(x+tw+2+w, y+0, 1, 1, '╗')
	fill(x+tw+2+w, y+1, 1, 1, '║')
	fill(x+tw+2+w, y+2, 1, 1, '╝')
	text(x+tw+2, y+1, t)

	flush()
}

// UpdateTimer updates the timer of the status bar by a specified amount of
// seconds.
func (sb StatusBar) UpdateTimer(seconds int) {
	text(sb.X+1, sb.Y+1, FormatTimer(seconds))
	flush()
}

// UpdateText updates the text of the status bar.
func (sb StatusBar) UpdateText(t string) {
	//Text(sb.X+sb.timerWidth+2, sb.Y+1, text[0:sb.Width]) // text[0:sb.Width] panics for some reason
	fill(sb.X+sb.timerWidth+2, sb.Y+1, sb.Width, 1, ' ')
	text(sb.X+sb.timerWidth+2, sb.Y+1, t)
	flush()
}

// UpdateTimer is a helper function that updates the status bar's timer.
func UpdateTimer(seconds int) {
	if statusBar != nil {
		statusBar.UpdateTimer(seconds)
	}
}

// ResetTimer clears the status bar's timer.
func ResetTimer() {
	if statusBar != nil {
		fill(statusBar.X+1, statusBar.Y+1, statusBar.timerWidth, 1, ' ')
		flush()
	}
}

// UpdateText is a helper function that updates the status bar's text.
func UpdateText(text string) {
	if statusBar != nil {
		statusBar.UpdateText(text)
	}
}

// ResetText clears the status bar's text.
func ResetText() {
	if statusBar != nil {
		statusBar.UpdateText(statusBarDefaultText)
	}
}

// Capture represents a captured key press at a specific second of the timer
// along with the value of Hz that was recorded.
type Capture struct {
	Value   rune
	Seconds int
	Hz      float64
}

// Label returns the description of the captured key value.
func (c *Capture) Label() string {
	return Labels[c.Value]
}

// Timestamp returns the timestamp of when a capture happened.
func (c *Capture) Timestamp() string {
	return FormatTimer(c.Seconds)
}
<|endoftext|>


<commit_before>package bimg

/*
#cgo pkg-config: vips
#include "vips/vips.h"
*/
import "C"

import (
	"errors"
	"fmt"
	"math"
)

func Resize(buf []byte, o Options) ([]byte, error) {
	defer C.vips_thread_shutdown()

	if len(buf) == 0 {
		return nil, errors.New("Image buffer is empty")
	}

	image, imageType, err := vipsRead(buf)
	if err != nil {
		return nil, err
	}

	// Defaults
	if o.Quality == 0 {
		o.Quality = QUALITY
	}
	if o.Compression == 0 {
		o.Compression = 6
	}
	if o.Type == 0 {
		o.Type = imageType
	}

	if IsTypeSupported(o.Type) == false {
		return nil, errors.New("Unsupported image output type")
	}

	debug("Options: %#v", o)

	inWidth := int(image.Xsize)
	inHeight := int(image.Ysize)

	// image calculations
	factor := imageCalculations(&o, inWidth, inHeight)
	shrink := int(math.Max(math.Floor(factor), 1))
	residual := float64(shrink) / factor

	// Do not enlarge the output if the input width *or* height are already less than the required dimensions
	if o.Enlarge == false {
		if inWidth < o.Width && inHeight < o.Height {
			factor = 1.0
			shrink = 1
			residual = 0
			o.Width = inWidth
			o.Height = inHeight
		}
	}

	// Try to use libjpeg shrink-on-load
	if imageType == JPEG && shrink >= 2 {
		tmpImage, factor, err := shrinkJpegImage(buf, image, factor, shrink)
		if err != nil {
			return nil, err
		}
		if tmpImage != nil {
			image = tmpImage
			factor = math.Max(factor, 1.0)
			shrink = int(math.Floor(factor))
			residual = float64(shrink) / factor
		}
	}

	// Calculate integral box shrink
	windowSize := vipsWindowSize(o.Interpolator.String())
	if factor >= 2 && windowSize > 3 {
		// Shrink less, affine more with interpolators that use at least 4x4 pixel window, e.g. bicubic
		shrink = int(math.Max(float64(math.Floor(factor*3.0/windowSize)), 1))
	}

	// Transform image if necessary
	debug(">>>>> Resolution %dx%d -> %dx%d", o.Width, o.Height, inWidth, inHeight)

	shouldExtract := o.Width != inWidth || o.Height != inHeight || o.AreaWidth > 0 || o.AreaHeight > 0
	if shouldExtract {
		// Use vips_shrink with the integral reduction
		if shrink > 1 {
			image, residual, err = shrinkImage(image, o, residual, shrink)
			if err != nil {
				return nil, err
			}
		}
		// Use vips_affine with the remaining float part
		if residual != 0 {
			image, err = vipsAffine(image, residual, o.Interpolator)
			if err != nil {
				return nil, err
			}
		}
		debug("factor: %v, shrink: %v, residual: %v", factor, shrink, residual)

		// Extract area from image
		image, err = extractImage(image, o)
		if err != nil {
			return nil, err
		}
	}

	// Rotate / flip image if necessary based on EXIF metadata
	image, err = rotateImage(image, o)
	if err != nil {
		return nil, err
	}

	saveOptions := vipsSaveOptions{
		Quality:     o.Quality,
		Type:        o.Type,
		Compression: o.Compression,
	}

	// Finally save as buffer
	buf, err = vipsSave(image, saveOptions)
	if err != nil {
		return nil, err
	}

	return buf, nil
}

func extractImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, error) {
	var err error = nil
	inWidth := int(image.Xsize)
	inHeight := int(image.Ysize)

	switch {
	case o.Crop:
		width := int(math.Min(float64(inWidth), float64(o.Width)))
		height := int(math.Min(float64(inHeight), float64(o.Height)))
		left, top := calculateCrop(inWidth, inHeight, o.Width, o.Height, o.Gravity)
		image, err = vipsExtract(image, left, top, width, height)
		break
	case o.Embed:
		left, top := (o.Width-inWidth)/2, (o.Height-inHeight)/2
		image, err = vipsEmbed(image, left, top, o.Width, o.Height, o.Extend)
		break
	case o.Top > 0 || o.Left > 0:
		if o.AreaWidth == 0 || o.AreaHeight == 0 {
			err = errors.New(fmt.Sprintf("Invalid area to extract %dx%d", o.AreaWidth, o.AreaHeight))
		} else {
			image, err = vipsExtract(image, o.Left, o.Top, o.AreaWidth, o.AreaHeight)
		}
		break
	}

	return image, err
}

func rotateImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, error) {
	var err error
	var direction Direction = -1

	rotation, flip := calculateRotationAndFlip(image, o.Rotate)
	if flip {
		o.Flip = flip
	}
	if rotation > D0 && o.Rotate == 0 {
		o.Rotate = rotation
	}

	if o.Rotate > 0 {
		image, err = vipsRotate(image, getAngle(o.Rotate))
	}

	if o.Flip {
		direction = HORIZONTAL
	} else if o.Flop {
		direction = VERTICAL
	}

	if direction != -1 {
		image, err = vipsFlip(image, direction)
	}

	return image, err
}

func shrinkImage(image *C.struct__VipsImage, o Options, residual float64, shrink int) (*C.struct__VipsImage, float64, error) {
	// Use vips_shrink with the integral reduction
	image, err := vipsShrink(image, shrink)
	if err != nil {
		return nil, 0, err
	}

	// Recalculate residual float based on dimensions of required vs shrunk images
	residualx := float64(o.Width) / float64(image.Xsize)
	residualy := float64(o.Height) / float64(image.Ysize)

	if o.Crop {
		residual = math.Max(residualx, residualy)
	} else {
		residual = math.Min(residualx, residualy)
	}

	return image, residual, nil
}

func shrinkJpegImage(buf []byte, input *C.struct__VipsImage, factor float64, shrink int) (*C.struct__VipsImage, float64, error) {
	var image *C.struct__VipsImage
	var err error
	shrinkOnLoad := 1

	// Recalculate integral shrink and double residual
	switch {
	case shrink >= 8:
		factor = factor / 8
		shrinkOnLoad = 8
	case shrink >= 4:
		factor = factor / 4
		shrinkOnLoad = 4
	case shrink >= 2:
		factor = factor / 2
		shrinkOnLoad = 2
	}

	// Reload input using shrink-on-load
	if shrinkOnLoad > 1 {
		image, err = vipsShrinkJpeg(buf, input, shrinkOnLoad)
	}

	return image, factor, err
}

func imageCalculations(o *Options, inWidth, inHeight int) float64 {
	factor := 1.0
	xfactor := float64(inWidth) / float64(o.Width)
	yfactor := float64(inHeight) / float64(o.Height)
	defer debug("Image calculations: %dx%d", o.Width, o.Height)

	switch {
	// Fixed width and height
	case o.Width > 0 && o.Height > 0:
		if o.Crop {
			factor = math.Min(xfactor, yfactor)
		} else {
			factor = math.Max(xfactor, yfactor)
		}
	// Fixed width, auto height
	case o.Width > 0:
		factor = xfactor
		o.Height = int(math.Floor(float64(inHeight) / factor))
	// Fixed height, auto width
	case o.Height > 0:
		factor = yfactor
		o.Width = int(math.Floor(float64(inWidth) / factor))
	default:
		// Identity transform
		o.Width = inWidth
		o.Height = inHeight
		break
	}

	return factor
}

func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) {
	left, top := 0, 0

	switch gravity {
	case NORTH:
		left = (inWidth - outWidth + 1) / 2
	case EAST:
		left = inWidth - outWidth
		top = (inHeight - outHeight + 1) / 2
	case SOUTH:
		left = (inWidth - outWidth + 1) / 2
		top = inHeight - outHeight
	case WEST:
		top = (inHeight - outHeight + 1) / 2
	default:
		left = (inWidth - outWidth + 1) / 2
		top = (inHeight - outHeight + 1) / 2
	}

	return left, top
}

func calculateRotationAndFlip(image *C.struct__VipsImage, angle Angle) (Angle, bool) {
	rotate := D0
	flip := false

	if angle == -1 {
		switch vipsExifOrientation(image) {
		case 6:
			rotate = D90
			break
		case 3:
			rotate = D180
			break
		case 8:
			rotate = D270
			break
		case 2:
			flip = true
			break // flip 1
		case 7:
			flip = true
			rotate = D90
			break // flip 6
		case 4:
			flip = true
			rotate = D180
			break // flip 3
		case 5:
			flip = true
			rotate = D270
			break // flip 8
		}
	} else {
		if angle == 90 {
			rotate = D90
		} else if angle == 180 {
			rotate = D180
		} else if angle == 270 {
			rotate = D270
		}
	}

	return rotate, flip
}

func getAngle(angle Angle) Angle {
	divisor := angle % 90
	if divisor != 0 {
		angle = angle - divisor
	}
	return angle
}
<commit_msg>refactor(resize): remove debug statement<commit_after>package bimg

/*
#cgo pkg-config: vips
#include "vips/vips.h"
*/
import "C"

import (
	"errors"
	"fmt"
	"math"
)

func Resize(buf []byte, o Options) ([]byte, error) {
	defer C.vips_thread_shutdown()

	if len(buf) == 0 {
		return nil, errors.New("Image buffer is empty")
	}

	image, imageType, err := vipsRead(buf)
	if err != nil {
		return nil, err
	}

	// Defaults
	if o.Quality == 0 {
		o.Quality = QUALITY
	}
	if o.Compression == 0 {
		o.Compression = 6
	}
	if o.Type == 0 {
		o.Type = imageType
	}

	if IsTypeSupported(o.Type) == false {
		return nil, errors.New("Unsupported image output type")
	}

	debug("Options: %#v", o)

	inWidth := int(image.Xsize)
	inHeight := int(image.Ysize)

	// image calculations
	factor := imageCalculations(&o, inWidth, inHeight)
	shrink := int(math.Max(math.Floor(factor), 1))
	residual := float64(shrink) / factor

	// Do not enlarge the output if the input width *or* height are already less than the required dimensions
	if o.Enlarge == false {
		if inWidth < o.Width && inHeight < o.Height {
			factor = 1.0
			shrink = 1
			residual = 0
			o.Width = inWidth
			o.Height = inHeight
		}
	}

	// Try to use libjpeg shrink-on-load
	if imageType == JPEG && shrink >= 2 {
		tmpImage, factor, err := shrinkJpegImage(buf, image, factor, shrink)
		if err != nil {
			return nil, err
		}
		if tmpImage != nil {
			image = tmpImage
			factor = math.Max(factor, 1.0)
			shrink = int(math.Floor(factor))
			residual = float64(shrink) / factor
		}
	}

	// Calculate integral box shrink
	windowSize := vipsWindowSize(o.Interpolator.String())
	if factor >= 2 && windowSize > 3 {
		// Shrink less, affine more with interpolators that use at least 4x4 pixel window, e.g. bicubic
		shrink = int(math.Max(float64(math.Floor(factor*3.0/windowSize)), 1))
	}

	// Transform image if necessary
	shouldTransform := o.Width != inWidth || o.Height != inHeight || o.AreaWidth > 0 || o.AreaHeight > 0
	if shouldTransform {
		// Use vips_shrink with the integral reduction
		if shrink > 1 {
			image, residual, err = shrinkImage(image, o, residual, shrink)
			if err != nil {
				return nil, err
			}
		}
		// Use vips_affine with the remaining float part
		if residual != 0 {
			image, err = vipsAffine(image, residual, o.Interpolator)
			if err != nil {
				return nil, err
			}
		}

		debug("Transform image: factor=%v, shrink=%v, residual=%v", factor, shrink, residual)
		// Extract area from image
		image, err = extractImage(image, o)
		if err != nil {
			return nil, err
		}
	}

	// Rotate / flip image if necessary based on EXIF metadata
	image, err = rotateImage(image, o)
	if err != nil {
		return nil, err
	}

	saveOptions := vipsSaveOptions{
		Quality:     o.Quality,
		Type:        o.Type,
		Compression: o.Compression,
	}

	// Finally save as buffer
	buf, err = vipsSave(image, saveOptions)
	if err != nil {
		return nil, err
	}

	return buf, nil
}

func extractImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, error) {
	var err error = nil
	inWidth := int(image.Xsize)
	inHeight := int(image.Ysize)

	switch {
	case o.Crop:
		width := int(math.Min(float64(inWidth), float64(o.Width)))
		height := int(math.Min(float64(inHeight), float64(o.Height)))
		left, top := calculateCrop(inWidth, inHeight, o.Width, o.Height, o.Gravity)
		image, err = vipsExtract(image, left, top, width, height)
		break
	case o.Embed:
		left, top := (o.Width-inWidth)/2, (o.Height-inHeight)/2
		image, err = vipsEmbed(image, left, top, o.Width, o.Height, o.Extend)
		break
	case o.Top > 0 || o.Left > 0:
		if o.AreaWidth == 0 || o.AreaHeight == 0 {
			err = errors.New(fmt.Sprintf("Invalid area to extract %dx%d", o.AreaWidth, o.AreaHeight))
		} else {
			image, err = vipsExtract(image, o.Left, o.Top, o.AreaWidth, o.AreaHeight)
		}
		break
	}

	return image, err
}

func rotateImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, error) {
	var err error
	var direction Direction = -1

	rotation, flip := calculateRotationAndFlip(image, o.Rotate)
	if flip {
		o.Flip = flip
	}
	if rotation > D0 && o.Rotate == 0 {
		o.Rotate = rotation
	}

	if o.Rotate > 0 {
		image, err = vipsRotate(image, getAngle(o.Rotate))
	}

	if o.Flip {
		direction = HORIZONTAL
	} else if o.Flop {
		direction = VERTICAL
	}

	if direction != -1 {
		image, err = vipsFlip(image, direction)
	}

	return image, err
}

func shrinkImage(image *C.struct__VipsImage, o Options, residual float64, shrink int) (*C.struct__VipsImage, float64, error) {
	// Use vips_shrink with the integral reduction
	image, err := vipsShrink(image, shrink)
	if err != nil {
		return nil, 0, err
	}

	// Recalculate residual float based on dimensions of required vs shrunk images
	residualx := float64(o.Width) / float64(image.Xsize)
	residualy := float64(o.Height) / float64(image.Ysize)

	if o.Crop {
		residual = math.Max(residualx, residualy)
	} else {
		residual = math.Min(residualx, residualy)
	}

	return image, residual, nil
}

func shrinkJpegImage(buf []byte, input *C.struct__VipsImage, factor float64, shrink int) (*C.struct__VipsImage, float64, error) {
	var image *C.struct__VipsImage
	var err error
	shrinkOnLoad := 1

	// Recalculate integral shrink and double residual
	switch {
	case shrink >= 8:
		factor = factor / 8
		shrinkOnLoad = 8
	case shrink >= 4:
		factor = factor / 4
		shrinkOnLoad = 4
	case shrink >= 2:
		factor = factor / 2
		shrinkOnLoad = 2
	}

	// Reload input using shrink-on-load
	if shrinkOnLoad > 1 {
		image, err = vipsShrinkJpeg(buf, input, shrinkOnLoad)
	}

	return image, factor, err
}

func imageCalculations(o *Options, inWidth, inHeight int) float64 {
	factor := 1.0
	xfactor := float64(inWidth) / float64(o.Width)
	yfactor := float64(inHeight) / float64(o.Height)
	defer debug("Image calculations: %dx%d", o.Width, o.Height)

	switch {
	// Fixed width and height
	case o.Width > 0 && o.Height > 0:
		if o.Crop {
			factor = math.Min(xfactor, yfactor)
		} else {
			factor = math.Max(xfactor, yfactor)
		}
	// Fixed width, auto height
	case o.Width > 0:
		factor = xfactor
		o.Height = int(math.Floor(float64(inHeight) / factor))
	// Fixed height, auto width
	case o.Height > 0:
		factor = yfactor
		o.Width = int(math.Floor(float64(inWidth) / factor))
	default:
		// Identity transform
		o.Width = inWidth
		o.Height = inHeight
		break
	}

	return factor
}

func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) {
	left, top := 0, 0

	switch gravity {
	case NORTH:
		left = (inWidth - outWidth + 1) / 2
	case EAST:
		left = inWidth - outWidth
		top = (inHeight - outHeight + 1) / 2
	case SOUTH:
		left = (inWidth - outWidth + 1) / 2
		top = inHeight - outHeight
	case WEST:
		top = (inHeight - outHeight + 1) / 2
	default:
		left = (inWidth - outWidth + 1) / 2
		top = (inHeight - outHeight + 1) / 2
	}

	return left, top
}

func calculateRotationAndFlip(image *C.struct__VipsImage, angle Angle) (Angle, bool) {
	rotate := D0
	flip := false

	if angle == -1 {
		switch vipsExifOrientation(image) {
		case 6:
			rotate = D90
			break
		case 3:
			rotate = D180
			break
		case 8:
			rotate = D270
			break
		case 2:
			flip = true
			break // flip 1
		case 7:
			flip = true
			rotate = D90
			break // flip 6
		case 4:
			flip = true
			rotate = D180
			break // flip 3
		case 5:
			flip = true
			rotate = D270
			break // flip 8
		}
	} else {
		if angle == 90 {
			rotate = D90
		} else if angle == 180 {
			rotate = D180
		} else if angle == 270 {
			rotate = D270
		}
	}

	return rotate, flip
}

func getAngle(angle Angle) Angle {
	divisor := angle % 90
	if divisor != 0 {
		angle = angle - divisor
	}
	return angle
}
<|endoftext|>


<commit_before>package proto

const (
	StatusOK             = 200
	StatusAccepted       = 202
	StatusPartial        = 206
	StatusBadRequest     = 400
	StatusUnauthorized   = 401
	StatusForbidden      = 403
	StatusNotFound       = 404
	StatusConflict       = 406
	StatusError          = 500
	StatusNotImplemented = 501
	StatusGatewayTimeout = 504
)

// Display text for status codes
var DisplayStatus = map[int]string{
	200: "OK",
	202: "Accepted",
	206: "Partial result",
	400: "Bad Request",
	401: "Unauthorized",
	403: "Forbidden",
	404: "Not found",
	406: "Readonly instance",
	500: "Server error",
	501: "Not implemented",
	504: "Gateway timeout",
}

type Result struct {
	StatusCode uint16 `json:"statusCode"`
	StatusText string `json:"statusText"`

	// Errors is set for StatusCode >399
	Errors *[]string `json:"errors,omitempty"`
	// JobId is set for StatusCode 202 (async processing)
	JobId string `json:"jobId,omitempty"`
	// List of (outstanding) deployment IDs
	DeploymentsList *[]string `json:"deploymentsList,omitempty"`

	// Request dependent data
	Attributes       *[]Attribute       `json:"attributes,omitempty"`
	Buckets          *[]Bucket          `json:"buckets,omitempty"`
	Capabilities     *[]Capability      `json:"capability,omitempty"`
	Categories       *[]Category        `json:"categories,omitempty"`
	CheckConfigs     *[]CheckConfig     `json:"checkConfigs,omitempty"`
	Clusters         *[]Cluster         `json:"clusters,omitempty"`
	DatacenterGroups *[]DatacenterGroup `json:"datacenterGroups,omitempty"`
	Datacenters      *[]Datacenter      `json:"datacenter,omitempty"`
	Deployments      *[]Deployment      `json:"deployments,omitempty"`
	Entities         *[]Entity          `json:"entities,omitempty"`
	Environments     *[]Environment     `json:"environment,omitempty"`
	Grants           *[]Grant           `json:"grants,omitempty"`
	Groups           *[]Group           `json:"groups,omitempty"`
	HostDeployments  *[]HostDeployment  `json:"hostDeployments,omitempty"`
	Levels           *[]Level           `json:"levels,omitempty"`
	Metrics          *[]Metric          `json:"metrics,omitempty"`
	Modes            *[]Mode            `json:"modes,omitempty"`
	Monitorings      *[]Monitoring      `json:"monitorings,omitempty"`
	Nodes            *[]Node            `json:"nodes,omitempty"`
	Oncalls          *[]Oncall          `json:"oncall,omitempty"`
	Permissions      *[]Permission      `json:"permissions,omitempty"`
	Predicates       *[]Predicate       `json:"predicates,omitempty"`
	Properties       *[]Property        `json:"properties,omitempty"`
	Providers        *[]Provider        `json:"providers,omitempty"`
	Repositories     *[]Repository      `json:"repositories,omitempty"`
	Servers          *[]Server          `json:"servers,omitempty"`
	States           *[]State           `json:"states,omitempty"`
	Status           *[]Status          `json:"status,omitempty"`
	Teams            *[]Team            `json:"teams,omitempty"`
	Units            *[]Unit            `json:"units,omitempty"`
	Users            *[]User            `json:"users,omitempty"`
	Validities       *[]Validity        `json:"validities,omitempty"`
	Views            *[]View            `json:"views,omitempty"`
}

func (r *Result) Error(err error) bool {
	if err != nil {
		r.StatusCode = StatusError
		r.StatusText = DisplayStatus[StatusError]
		r.Errors = &[]string{err.Error()}
		return true
	}
	return false
}

func (r *Result) Conflict() {
	r.StatusCode = StatusConflict
	r.StatusText = DisplayStatus[StatusConflict]
}

func (r *Result) NotImplemented() {
	r.StatusCode = StatusNotImplemented
	r.StatusText = DisplayStatus[StatusNotImplemented]
}

func (r *Result) NotFound() {
	r.StatusCode = StatusNotFound
	r.StatusText = DisplayStatus[StatusNotFound]
}

func (r *Result) Accepted() {
	r.StatusCode = StatusAccepted
	r.StatusText = DisplayStatus[StatusAccepted]
}

func (r *Result) OK() {
	if r.Errors == nil || *r.Errors == nil || len(*r.Errors) == 0 {
		r.StatusCode = StatusOK
		r.StatusText = DisplayStatus[StatusOK]
		return
	}
	r.Partial()
}

func (r *Result) Partial() {
	r.StatusCode = StatusPartial
	r.StatusText = DisplayStatus[StatusPartial]
}

func (r *Result) Clean() {
	if r.Errors == nil || len(*r.Errors) == 0 {
		r.Errors = nil
	}

	if r.DeploymentsList == nil || len(*r.DeploymentsList) == 0 {
		r.DeploymentsList = nil
	}
}

// Legacy interface
func (r *Result) ErrorMark(err error, imp bool, found bool,
	length int, jobid string) bool {

	if r.Error(err) {
		return true
	}
	if imp {
		r.NotImplemented()
		return true
	}
	if found || length == 0 {
		r.NotFound()
		return true
	}
	if jobid != "" {
		r.JobId = jobid
		r.Accepted()
		return true
	}
	r.OK()
	return false
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Add JobType to result<commit_after>package proto

const (
	StatusOK             = 200
	StatusAccepted       = 202
	StatusPartial        = 206
	StatusBadRequest     = 400
	StatusUnauthorized   = 401
	StatusForbidden      = 403
	StatusNotFound       = 404
	StatusConflict       = 406
	StatusError          = 500
	StatusNotImplemented = 501
	StatusGatewayTimeout = 504
)

// Display text for status codes
var DisplayStatus = map[int]string{
	200: "OK",
	202: "Accepted",
	206: "Partial result",
	400: "Bad Request",
	401: "Unauthorized",
	403: "Forbidden",
	404: "Not found",
	406: "Readonly instance",
	500: "Server error",
	501: "Not implemented",
	504: "Gateway timeout",
}

type Result struct {
	StatusCode uint16 `json:"statusCode"`
	StatusText string `json:"statusText"`

	// Errors is set for StatusCode >399
	Errors *[]string `json:"errors,omitempty"`
	// JobId is set for StatusCode 202 (async processing)
	JobId   string `json:"jobId,omitempty"`
	JobType string `json:"jobType,omitempty"`
	// List of (outstanding) deployment IDs
	DeploymentsList *[]string `json:"deploymentsList,omitempty"`

	// Request dependent data
	Attributes       *[]Attribute       `json:"attributes,omitempty"`
	Buckets          *[]Bucket          `json:"buckets,omitempty"`
	Capabilities     *[]Capability      `json:"capability,omitempty"`
	Categories       *[]Category        `json:"categories,omitempty"`
	CheckConfigs     *[]CheckConfig     `json:"checkConfigs,omitempty"`
	Clusters         *[]Cluster         `json:"clusters,omitempty"`
	DatacenterGroups *[]DatacenterGroup `json:"datacenterGroups,omitempty"`
	Datacenters      *[]Datacenter      `json:"datacenter,omitempty"`
	Deployments      *[]Deployment      `json:"deployments,omitempty"`
	Entities         *[]Entity          `json:"entities,omitempty"`
	Environments     *[]Environment     `json:"environment,omitempty"`
	Grants           *[]Grant           `json:"grants,omitempty"`
	Groups           *[]Group           `json:"groups,omitempty"`
	HostDeployments  *[]HostDeployment  `json:"hostDeployments,omitempty"`
	Levels           *[]Level           `json:"levels,omitempty"`
	Metrics          *[]Metric          `json:"metrics,omitempty"`
	Modes            *[]Mode            `json:"modes,omitempty"`
	Monitorings      *[]Monitoring      `json:"monitorings,omitempty"`
	Nodes            *[]Node            `json:"nodes,omitempty"`
	Oncalls          *[]Oncall          `json:"oncall,omitempty"`
	Permissions      *[]Permission      `json:"permissions,omitempty"`
	Predicates       *[]Predicate       `json:"predicates,omitempty"`
	Properties       *[]Property        `json:"properties,omitempty"`
	Providers        *[]Provider        `json:"providers,omitempty"`
	Repositories     *[]Repository      `json:"repositories,omitempty"`
	Servers          *[]Server          `json:"servers,omitempty"`
	States           *[]State           `json:"states,omitempty"`
	Status           *[]Status          `json:"status,omitempty"`
	Teams            *[]Team            `json:"teams,omitempty"`
	Units            *[]Unit            `json:"units,omitempty"`
	Users            *[]User            `json:"users,omitempty"`
	Validities       *[]Validity        `json:"validities,omitempty"`
	Views            *[]View            `json:"views,omitempty"`
}

func (r *Result) Error(err error) bool {
	if err != nil {
		r.StatusCode = StatusError
		r.StatusText = DisplayStatus[StatusError]
		r.Errors = &[]string{err.Error()}
		return true
	}
	return false
}

func (r *Result) Conflict() {
	r.StatusCode = StatusConflict
	r.StatusText = DisplayStatus[StatusConflict]
}

func (r *Result) NotImplemented() {
	r.StatusCode = StatusNotImplemented
	r.StatusText = DisplayStatus[StatusNotImplemented]
}

func (r *Result) NotFound() {
	r.StatusCode = StatusNotFound
	r.StatusText = DisplayStatus[StatusNotFound]
}

func (r *Result) Accepted() {
	r.StatusCode = StatusAccepted
	r.StatusText = DisplayStatus[StatusAccepted]
}

func (r *Result) OK() {
	if r.Errors == nil || *r.Errors == nil || len(*r.Errors) == 0 {
		r.StatusCode = StatusOK
		r.StatusText = DisplayStatus[StatusOK]
		return
	}
	r.Partial()
}

func (r *Result) Partial() {
	r.StatusCode = StatusPartial
	r.StatusText = DisplayStatus[StatusPartial]
}

func (r *Result) Clean() {
	if r.Errors == nil || len(*r.Errors) == 0 {
		r.Errors = nil
	}

	if r.DeploymentsList == nil || len(*r.DeploymentsList) == 0 {
		r.DeploymentsList = nil
	}
}

// Legacy interface
func (r *Result) ErrorMark(err error, imp bool, found bool,
	length int, jobid, jobtype string) bool {

	if r.Error(err) {
		return true
	}
	if imp {
		r.NotImplemented()
		return true
	}
	if found || length == 0 {
		r.NotFound()
		return true
	}
	if jobid != "" {
		r.JobId = jobid
		r.JobType = jobtype
		r.Accepted()
		return true
	}
	r.OK()
	return false
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package vulcand

import (
	"encoding/json"
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/albertrdixon/gearbox/logger"
	"github.com/albertrdixon/gearbox/url"
	"github.com/timelinelabs/vulcand/api"
	"github.com/timelinelabs/vulcand/engine"
	"github.com/timelinelabs/vulcand/plugin"
	"github.com/timelinelabs/vulcand/plugin/registry"
	vroute "github.com/vulcand/route"
	"golang.org/x/net/context"

	"github.com/timelinelabs/romulus/kubernetes"
	"github.com/timelinelabs/romulus/loadbalancer"
	"k8s.io/kubernetes/pkg/api/resource"
)

const (
	DefaultRoute             = "Path(`/`)"
	DefaultFailoverPredicate = `IsNetworkError() && Attempts() <= 2`

	DailTimeoutKey             = "dial_timeout"
	ReadTimeoutKey             = "read_timeout"
	MaxIdleConnsKey            = "max_idle_conns_per_host"
	CustomMiddlewareKeyPattern = `^middleware\.([^\.]+)`

	websocket = "websocket"
	ws        = "ws"
	HTTP      = "http"
	Enabled   = "enabled"

	RedirectSSLID = "redirect_to_ssl"
	TraceID       = "trace"
	AuthID        = "auth"
	MaintenanceID = "maintenance"
)

func New(vulcanURL string, reg *plugin.Registry, ctx context.Context) (*vulcan, error) {
	if _, er := url.Parse(vulcanURL); er != nil {
		return nil, er
	}

	if reg == nil {
		reg = registry.GetRegistry()
	}
	client := api.NewClient(vulcanURL, reg)
	if client == nil {
		return nil, errors.New("Failed to create vulcand client")
	}
	return &vulcan{Client: *client, c: ctx}, nil
}

func (v *vulcan) Kind() string {
	return "vulcand"
}

func (v *vulcan) Status() error {
	return v.GetStatus()
}

func (v *vulcan) NewFrontend(rsc *kubernetes.Resource) (loadbalancer.Frontend, error) {
	var (
		s = engine.HTTPFrontendSettings{
			PassHostHeader:     loadbalancer.DefaultPassHostHeader,
			TrustForwardHeader: loadbalancer.DefaultTrustForwardHeaders,
		}
		limits = engine.HTTPFrontendLimits{
			MaxBodyBytes:        int64(2097152),
			MaxMemBodyBytes:     int64(2097152),
			MaxRespBodyBytes:    int64(2097152),
			MaxRespMemBodyBytes: int64(2097152),
		}
	)

	if val, ok := rsc.GetAnnotation(loadbalancer.PassHostHeaderKey); ok {
		b, _ := strconv.ParseBool(val)
		s.PassHostHeader = b
	}
	if val, ok := rsc.GetAnnotation(loadbalancer.TrustForwardHeadersKey); ok {
		b, _ := strconv.ParseBool(val)
		s.TrustForwardHeader = b
	}
	if val, ok := rsc.GetAnnotation(loadbalancer.FailoverExpressionKey); ok {
		s.FailoverPredicate = val
	}

	if val, ok := rsc.GetAnnotation(loadbalancer.MaxReqSizeKey); ok {
		if q, er := resource.ParseQuantity(val); er == nil {
			limits.MaxBodyBytes = q.Value()
			limits.MaxMemBodyBytes = q.Value()
		} else {
			logger.Warnf("Failed to parse request limits: %v", er)
		}
	}
	if val, ok := rsc.GetAnnotation(loadbalancer.MaxRespSizeKey); ok {
		if q, er := resource.ParseQuantity(val); er == nil {
			limits.MaxRespBodyBytes = q.Value()
			limits.MaxRespMemBodyBytes = q.Value()
		} else {
			logger.Warnf("Failed to parse response limits: %v", er)
		}
	}
	s.Limits = limits

	if val, ok := rsc.GetAnnotation(loadbalancer.FrontendSettingsKey); ok {
		if er := json.Unmarshal([]byte(val), &s); er != nil {
			logger.Warnf("Failed to parse settings for frontend %q: %v", rsc.ID, er)
		}
	}

	f, er := engine.NewHTTPFrontend(vroute.NewMux(), rsc.ID(), rsc.ID(), NewRoute(rsc.Route).String(), s)
	if er != nil {
		return nil, er
	}
	return newFrontend(f), nil
}

func (v *vulcan) NewBackend(rsc *kubernetes.Resource) (loadbalancer.Backend, error) {
	s := engine.HTTPBackendSettings{
		Timeouts:  engine.HTTPBackendTimeouts{},
		KeepAlive: engine.HTTPBackendKeepAlive{},
	}
	if val, ok := rsc.GetAnnotation(DailTimeoutKey); ok {
		s.Timeouts.Dial = val
	}
	if val, ok := rsc.GetAnnotation(ReadTimeoutKey); ok {
		s.Timeouts.Read = val
	}
	if val, ok := rsc.GetAnnotation(MaxIdleConnsKey); ok {
		if i, er := strconv.Atoi(val); er == nil {
			s.KeepAlive.MaxIdleConnsPerHost = i
		}
	}
	if val, ok := rsc.GetAnnotation(loadbalancer.BackendSettingsKey); ok {
		if er := json.Unmarshal([]byte(val), &s); er != nil {
			logger.Warnf("Failed to parse settings for frontend %q: %v", rsc.ID, er)
		}
	}

	b, er := engine.NewHTTPBackend(rsc.ID(), s)
	if er != nil {
		return nil, er
	}
	if rsc.IsWebsocket() {
		b.Type = ws
	}
	return newBackend(b), nil
}

func (v *vulcan) NewServers(rsc *kubernetes.Resource) ([]loadbalancer.Server, error) {
	list := make([]loadbalancer.Server, 0, 1)
	for _, server := range rsc.Servers() {
		s, er := engine.NewServer(server.ID(), server.URL().String())
		if er != nil {
			return list, er
		}
		list = append(list, newServer(s))
	}
	return list, nil
}

func (v *vulcan) NewMiddlewares(rsc *kubernetes.Resource) ([]loadbalancer.Middleware, error) {
	mids := make([]loadbalancer.Middleware, 0, 1)
	for key, def := range DefaultMiddleware {
		if val, ok := rsc.GetAnnotation(key); ok && len(val) > 0 {
			switch key {
			case RedirectSSLID:
				if b, er := strconv.ParseBool(val); er != nil || !b {
					continue
				}
			case TraceID:
				re := regexp.MustCompile(`\s+`)
				list, er := json.Marshal(strings.Split(re.ReplaceAllString(val, ""), ","))
				if er != nil || string(list) == "" {
					logger.Warnf("Unable to json-ify trace headers: %v", er)
					list = []byte("[]")
				}
				def = fmt.Sprintf(def, string(list), string(list))
			case AuthID:
				bits := strings.SplitN(val, ":", 2)
				switch len(bits) {
				case 1:
					def = fmt.Sprintf(def, bits[0], "")
				case 2:
					def = fmt.Sprintf(def, bits[0], bits[1])
				default:
					logger.Errorf("Failed to parse provided basic auth, using default (admin:admin)")
					def = fmt.Sprintf(def, "admin", "admin")
				}
			case MaintenanceID:
				def = fmt.Sprintf(def, val)
			}

			m, er := engine.MiddlewareFromJSON([]byte(def), v.Registry.GetSpec, key)
			if er != nil {
				logger.Warnf("Failed to parse Middleware %s: %v", key, er)
				logger.Debugf("%q", def)
				continue
			}
			mids = append(mids, newMiddleware(m))
		}
	}

	rg := regexp.MustCompile(CustomMiddlewareKeyPattern)
	matches, _ := rsc.GetAnnotations(CustomMiddlewareKeyPattern)
	for key, val := range matches {
		if match := rg.FindStringSubmatch(key); match != nil {
			id := match[1]
			m, er := engine.MiddlewareFromJSON([]byte(val), v.Registry.GetSpec, id)
			if er != nil {
				logger.Warnf("Failed to parse Middleware %s: %v", id, er)
				continue
			}
			mids = append(mids, newMiddleware(m))
		}
	}

	return mids, nil
}

func (v *vulcan) UpsertFrontend(fr loadbalancer.Frontend) error {
	f, ok := fr.(*frontend)
	if !ok {
		return loadbalancer.ErrUnexpectedFrontendType
	}
	if er := v.Client.UpsertFrontend(f.Frontend, 0); er != nil {
		return er
	}
	for _, mid := range f.middlewares {
		if er := v.UpsertMiddleware(f.GetKey(), mid.Middleware, 0); er != nil {
			logger.Warnf("Failed to upsert Middleware %s for frontend %s: %v", mid.GetID(), f.GetID(), er)
		}
	}
	return nil
}

func (v *vulcan) UpsertBackend(ba loadbalancer.Backend) error {
	b, ok := ba.(*backend)
	if !ok {
		return loadbalancer.ErrUnexpectedBackendType
	}
	if er := v.Client.UpsertBackend(b.Backend); er != nil {
		return er
	}

	extra := make(map[string]loadbalancer.Server)
	ss, _ := v.Client.GetServers(engine.BackendKey{Id: b.GetID()})
	for i := range ss {
		extra[ss[i].GetId()] = &server{ss[i]}
	}
	for _, srv := range b.servers {
		logger.Infof("Upserting %v", srv)
		if er := v.UpsertServer(b, srv); er != nil {
			return er
		}
		delete(extra, srv.GetID())
	}
	for _, srv := range extra {
		logger.Infof("Removing %v", srv)
		v.DeleteServer(b, srv)
	}
	return nil
}

func (v *vulcan) UpsertServer(backend loadbalancer.Backend, srv loadbalancer.Server) error {
	return v.Client.UpsertServer(engine.BackendKey{Id: backend.GetID()}, srv.(*server).Server, 0)
}

func (v *vulcan) GetFrontend(frontendID string) (loadbalancer.Frontend, error) {
	f, er := v.Client.GetFrontend(engine.FrontendKey{Id: frontendID})
	if er != nil {
		return nil, er
	}

	return newFrontend(f), nil
}

func (v *vulcan) GetBackend(backendID string) (loadbalancer.Backend, error) {
	logger.Debugf("Lookup Backend: %q", backendID)
	b, er := v.Client.GetBackend(engine.BackendKey{Id: backendID})
	if er != nil {
		logger.Debugf("Lookup failed: %v", er)
		return nil, er
	}
	return newBackend(b), nil
}

func (v *vulcan) GetServers(backendID string) ([]loadbalancer.Server, error) {
	srvs, er := v.Client.GetServers(engine.BackendKey{Id: backendID})
	if er != nil {
		return []loadbalancer.Server{}, er
	}

	servers := make([]loadbalancer.Server, 0, len(srvs))
	for _, s := range srvs {
		servers = append(servers, newServer(&s))
	}
	return servers, nil
}

func (v *vulcan) DeleteFrontend(fr loadbalancer.Frontend) error {
	return v.Client.DeleteFrontend(engine.FrontendKey{Id: fr.GetID()})
}

func (v *vulcan) DeleteBackend(ba loadbalancer.Backend) error {
	return v.Client.DeleteBackend(engine.BackendKey{Id: ba.GetID()})
}

func (v *vulcan) DeleteServer(ba loadbalancer.Backend, srv loadbalancer.Server) error {
	return v.Client.DeleteServer(
		engine.ServerKey{BackendKey: engine.BackendKey{Id: ba.GetID()}, Id: srv.GetID()},
	)
}

func (b *backend) GetID() string             { return b.GetId() }
func (b *backend) GetKey() engine.BackendKey { return engine.BackendKey{Id: b.GetID()} }

func (b *backend) AddServer(srv loadbalancer.Server) {
	if b.servers == nil {
		b.servers = make([]*server, 0, 1)
	}
	b.servers = append(b.servers, srv.(*server))
}

func (f *frontend) GetID() string { return f.GetId() }

func (f *frontend) AddMiddleware(mid loadbalancer.Middleware) {
	if f.middlewares == nil {
		f.middlewares = make([]*middleware, 0, 1)
	}
	f.middlewares = append(f.middlewares, mid.(*middleware))
}

func (f *frontend) GetRoute() string {
	return f.Route
}

func (m *middleware) GetID() string { return m.Id }

func (s *server) GetID() string { return s.GetId() }
<commit_msg>default req/resp size 1Mi<commit_after>package vulcand

import (
	"encoding/json"
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/albertrdixon/gearbox/logger"
	"github.com/albertrdixon/gearbox/url"
	"github.com/timelinelabs/vulcand/api"
	"github.com/timelinelabs/vulcand/engine"
	"github.com/timelinelabs/vulcand/plugin"
	"github.com/timelinelabs/vulcand/plugin/registry"
	vroute "github.com/vulcand/route"
	"golang.org/x/net/context"

	"github.com/timelinelabs/romulus/kubernetes"
	"github.com/timelinelabs/romulus/loadbalancer"
	"k8s.io/kubernetes/pkg/api/resource"
)

const (
	DefaultRoute             = "Path(`/`)"
	DefaultFailoverPredicate = `IsNetworkError() && Attempts() <= 2`

	DailTimeoutKey             = "dial_timeout"
	ReadTimeoutKey             = "read_timeout"
	MaxIdleConnsKey            = "max_idle_conns_per_host"
	CustomMiddlewareKeyPattern = `^middleware\.([^\.]+)`

	websocket = "websocket"
	ws        = "ws"
	HTTP      = "http"
	Enabled   = "enabled"

	RedirectSSLID = "redirect_to_ssl"
	TraceID       = "trace"
	AuthID        = "auth"
	MaintenanceID = "maintenance"
)

func New(vulcanURL string, reg *plugin.Registry, ctx context.Context) (*vulcan, error) {
	if _, er := url.Parse(vulcanURL); er != nil {
		return nil, er
	}

	if reg == nil {
		reg = registry.GetRegistry()
	}
	client := api.NewClient(vulcanURL, reg)
	if client == nil {
		return nil, errors.New("Failed to create vulcand client")
	}
	return &vulcan{Client: *client, c: ctx}, nil
}

func (v *vulcan) Kind() string {
	return "vulcand"
}

func (v *vulcan) Status() error {
	return v.GetStatus()
}

func (v *vulcan) NewFrontend(rsc *kubernetes.Resource) (loadbalancer.Frontend, error) {
	var (
		s = engine.HTTPFrontendSettings{
			PassHostHeader:     loadbalancer.DefaultPassHostHeader,
			TrustForwardHeader: loadbalancer.DefaultTrustForwardHeaders,
		}
		limits = engine.HTTPFrontendLimits{
			MaxBodyBytes:        int64(1048576),
			MaxMemBodyBytes:     int64(1048576),
			MaxRespBodyBytes:    int64(1048576),
			MaxRespMemBodyBytes: int64(1048576),
		}
	)

	if val, ok := rsc.GetAnnotation(loadbalancer.PassHostHeaderKey); ok {
		b, _ := strconv.ParseBool(val)
		s.PassHostHeader = b
	}
	if val, ok := rsc.GetAnnotation(loadbalancer.TrustForwardHeadersKey); ok {
		b, _ := strconv.ParseBool(val)
		s.TrustForwardHeader = b
	}
	if val, ok := rsc.GetAnnotation(loadbalancer.FailoverExpressionKey); ok {
		s.FailoverPredicate = val
	}

	if val, ok := rsc.GetAnnotation(loadbalancer.MaxReqSizeKey); ok {
		if q, er := resource.ParseQuantity(val); er == nil {
			limits.MaxBodyBytes = q.Value()
			limits.MaxMemBodyBytes = q.Value()
		} else {
			logger.Warnf("Failed to parse request limits: %v", er)
		}
	}
	if val, ok := rsc.GetAnnotation(loadbalancer.MaxRespSizeKey); ok {
		if q, er := resource.ParseQuantity(val); er == nil {
			limits.MaxRespBodyBytes = q.Value()
			limits.MaxRespMemBodyBytes = q.Value()
		} else {
			logger.Warnf("Failed to parse response limits: %v", er)
		}
	}
	s.Limits = limits

	if val, ok := rsc.GetAnnotation(loadbalancer.FrontendSettingsKey); ok {
		if er := json.Unmarshal([]byte(val), &s); er != nil {
			logger.Warnf("Failed to parse settings for frontend %q: %v", rsc.ID, er)
		}
	}

	f, er := engine.NewHTTPFrontend(vroute.NewMux(), rsc.ID(), rsc.ID(), NewRoute(rsc.Route).String(), s)
	if er != nil {
		return nil, er
	}
	return newFrontend(f), nil
}

func (v *vulcan) NewBackend(rsc *kubernetes.Resource) (loadbalancer.Backend, error) {
	s := engine.HTTPBackendSettings{
		Timeouts:  engine.HTTPBackendTimeouts{},
		KeepAlive: engine.HTTPBackendKeepAlive{},
	}
	if val, ok := rsc.GetAnnotation(DailTimeoutKey); ok {
		s.Timeouts.Dial = val
	}
	if val, ok := rsc.GetAnnotation(ReadTimeoutKey); ok {
		s.Timeouts.Read = val
	}
	if val, ok := rsc.GetAnnotation(MaxIdleConnsKey); ok {
		if i, er := strconv.Atoi(val); er == nil {
			s.KeepAlive.MaxIdleConnsPerHost = i
		}
	}
	if val, ok := rsc.GetAnnotation(loadbalancer.BackendSettingsKey); ok {
		if er := json.Unmarshal([]byte(val), &s); er != nil {
			logger.Warnf("Failed to parse settings for frontend %q: %v", rsc.ID, er)
		}
	}

	b, er := engine.NewHTTPBackend(rsc.ID(), s)
	if er != nil {
		return nil, er
	}
	if rsc.IsWebsocket() {
		b.Type = ws
	}
	return newBackend(b), nil
}

func (v *vulcan) NewServers(rsc *kubernetes.Resource) ([]loadbalancer.Server, error) {
	list := make([]loadbalancer.Server, 0, 1)
	for _, server := range rsc.Servers() {
		s, er := engine.NewServer(server.ID(), server.URL().String())
		if er != nil {
			return list, er
		}
		list = append(list, newServer(s))
	}
	return list, nil
}

func (v *vulcan) NewMiddlewares(rsc *kubernetes.Resource) ([]loadbalancer.Middleware, error) {
	mids := make([]loadbalancer.Middleware, 0, 1)
	for key, def := range DefaultMiddleware {
		if val, ok := rsc.GetAnnotation(key); ok && len(val) > 0 {
			switch key {
			case RedirectSSLID:
				if b, er := strconv.ParseBool(val); er != nil || !b {
					continue
				}
			case TraceID:
				re := regexp.MustCompile(`\s+`)
				list, er := json.Marshal(strings.Split(re.ReplaceAllString(val, ""), ","))
				if er != nil || string(list) == "" {
					logger.Warnf("Unable to json-ify trace headers: %v", er)
					list = []byte("[]")
				}
				def = fmt.Sprintf(def, string(list), string(list))
			case AuthID:
				bits := strings.SplitN(val, ":", 2)
				switch len(bits) {
				case 1:
					def = fmt.Sprintf(def, bits[0], "")
				case 2:
					def = fmt.Sprintf(def, bits[0], bits[1])
				default:
					logger.Errorf("Failed to parse provided basic auth, using default (admin:admin)")
					def = fmt.Sprintf(def, "admin", "admin")
				}
			case MaintenanceID:
				def = fmt.Sprintf(def, val)
			}

			m, er := engine.MiddlewareFromJSON([]byte(def), v.Registry.GetSpec, key)
			if er != nil {
				logger.Warnf("Failed to parse Middleware %s: %v", key, er)
				logger.Debugf("%q", def)
				continue
			}
			mids = append(mids, newMiddleware(m))
		}
	}

	rg := regexp.MustCompile(CustomMiddlewareKeyPattern)
	matches, _ := rsc.GetAnnotations(CustomMiddlewareKeyPattern)
	for key, val := range matches {
		if match := rg.FindStringSubmatch(key); match != nil {
			id := match[1]
			m, er := engine.MiddlewareFromJSON([]byte(val), v.Registry.GetSpec, id)
			if er != nil {
				logger.Warnf("Failed to parse Middleware %s: %v", id, er)
				continue
			}
			mids = append(mids, newMiddleware(m))
		}
	}

	return mids, nil
}

func (v *vulcan) UpsertFrontend(fr loadbalancer.Frontend) error {
	f, ok := fr.(*frontend)
	if !ok {
		return loadbalancer.ErrUnexpectedFrontendType
	}
	if er := v.Client.UpsertFrontend(f.Frontend, 0); er != nil {
		return er
	}
	for _, mid := range f.middlewares {
		if er := v.UpsertMiddleware(f.GetKey(), mid.Middleware, 0); er != nil {
			logger.Warnf("Failed to upsert Middleware %s for frontend %s: %v", mid.GetID(), f.GetID(), er)
		}
	}
	return nil
}

func (v *vulcan) UpsertBackend(ba loadbalancer.Backend) error {
	b, ok := ba.(*backend)
	if !ok {
		return loadbalancer.ErrUnexpectedBackendType
	}
	if er := v.Client.UpsertBackend(b.Backend); er != nil {
		return er
	}

	extra := make(map[string]loadbalancer.Server)
	ss, _ := v.Client.GetServers(engine.BackendKey{Id: b.GetID()})
	for i := range ss {
		extra[ss[i].GetId()] = &server{ss[i]}
	}
	for _, srv := range b.servers {
		logger.Infof("Upserting %v", srv)
		if er := v.UpsertServer(b, srv); er != nil {
			return er
		}
		delete(extra, srv.GetID())
	}
	for _, srv := range extra {
		logger.Infof("Removing %v", srv)
		v.DeleteServer(b, srv)
	}
	return nil
}

func (v *vulcan) UpsertServer(backend loadbalancer.Backend, srv loadbalancer.Server) error {
	return v.Client.UpsertServer(engine.BackendKey{Id: backend.GetID()}, srv.(*server).Server, 0)
}

func (v *vulcan) GetFrontend(frontendID string) (loadbalancer.Frontend, error) {
	f, er := v.Client.GetFrontend(engine.FrontendKey{Id: frontendID})
	if er != nil {
		return nil, er
	}

	return newFrontend(f), nil
}

func (v *vulcan) GetBackend(backendID string) (loadbalancer.Backend, error) {
	logger.Debugf("Lookup Backend: %q", backendID)
	b, er := v.Client.GetBackend(engine.BackendKey{Id: backendID})
	if er != nil {
		logger.Debugf("Lookup failed: %v", er)
		return nil, er
	}
	return newBackend(b), nil
}

func (v *vulcan) GetServers(backendID string) ([]loadbalancer.Server, error) {
	srvs, er := v.Client.GetServers(engine.BackendKey{Id: backendID})
	if er != nil {
		return []loadbalancer.Server{}, er
	}

	servers := make([]loadbalancer.Server, 0, len(srvs))
	for _, s := range srvs {
		servers = append(servers, newServer(&s))
	}
	return servers, nil
}

func (v *vulcan) DeleteFrontend(fr loadbalancer.Frontend) error {
	return v.Client.DeleteFrontend(engine.FrontendKey{Id: fr.GetID()})
}

func (v *vulcan) DeleteBackend(ba loadbalancer.Backend) error {
	return v.Client.DeleteBackend(engine.BackendKey{Id: ba.GetID()})
}

func (v *vulcan) DeleteServer(ba loadbalancer.Backend, srv loadbalancer.Server) error {
	return v.Client.DeleteServer(
		engine.ServerKey{BackendKey: engine.BackendKey{Id: ba.GetID()}, Id: srv.GetID()},
	)
}

func (b *backend) GetID() string             { return b.GetId() }
func (b *backend) GetKey() engine.BackendKey { return engine.BackendKey{Id: b.GetID()} }

func (b *backend) AddServer(srv loadbalancer.Server) {
	if b.servers == nil {
		b.servers = make([]*server, 0, 1)
	}
	b.servers = append(b.servers, srv.(*server))
}

func (f *frontend) GetID() string { return f.GetId() }

func (f *frontend) AddMiddleware(mid loadbalancer.Middleware) {
	if f.middlewares == nil {
		f.middlewares = make([]*middleware, 0, 1)
	}
	f.middlewares = append(f.middlewares, mid.(*middleware))
}

func (f *frontend) GetRoute() string {
	return f.Route
}

func (m *middleware) GetID() string { return m.Id }

func (s *server) GetID() string { return s.GetId() }
<|endoftext|>