<commit_before>package memqueue

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/go-msgqueue/msgqueue"
	"github.com/go-msgqueue/msgqueue/internal"
)

var timers = sync.Pool{
	New: func() interface{} {
		t := time.NewTimer(time.Hour)
		t.Stop()
		return t
	},
}

type manager struct{}

func (manager) NewQueue(opt *msgqueue.Options) msgqueue.Queue {
	return NewQueue(opt)
}

func (manager) Queues() []msgqueue.Queue {
	var queues []msgqueue.Queue
	for _, q := range Queues() {
		queues = append(queues, q)
	}
	return queues
}

func NewManager() msgqueue.Manager {
	return manager{}
}

type Queue struct {
	opt *msgqueue.Options

	sync    bool
	noDelay bool

	p *msgqueue.Processor
}

var _ msgqueue.Queue = (*Queue)(nil)

func NewQueue(opt *msgqueue.Options) *Queue {
	opt.Init()

	q := Queue{
		opt: opt,
	}
	q.p = msgqueue.StartProcessor(&q, opt)

	registerQueue(&q)
	return &q
}

func (q *Queue) Name() string {
	return q.opt.Name
}

func (q *Queue) String() string {
	return fmt.Sprintf("Memqueue<Name=%s>", q.Name())
}

func (q *Queue) Options() *msgqueue.Options {
	return q.opt
}

func (q *Queue) Processor() *msgqueue.Processor {
	return q.p
}

func (q *Queue) SetSync(sync bool) {
	q.sync = sync
}

func (q *Queue) SetNoDelay(noDelay bool) {
	q.noDelay = noDelay
}

// Close is CloseTimeout with 30 seconds timeout.
func (q *Queue) Close() error {
	return q.CloseTimeout(30 * time.Second)
}

// Close closes the queue waiting for pending messages to be processed.
func (q *Queue) CloseTimeout(timeout time.Duration) error {
	defer unregisterQueue(q)
	return q.p.StopTimeout(timeout)
}

// Call creates a message using the args and adds it to the queue.
func (q *Queue) Call(args ...interface{}) error {
	msg := msgqueue.NewMessage(args...)
	return q.Add(msg)
}

// CallOnce works like Call, but it adds message with same args
// only once in a period.
func (q *Queue) CallOnce(period time.Duration, args ...interface{}) error {
	msg := msgqueue.NewMessage(args...)
	msg.SetDelayName(period, args...)
	return q.Add(msg)
}

// Add adds message to the queue.
func (q *Queue) Add(msg *msgqueue.Message) error {
	_, err := msg.GetBody()
	if err != nil {
		return err
	}

	if !q.isUniqueName(msg.Name) {
		return msgqueue.ErrDuplicate
	}
	return q.enqueueMessage(msg)
}

func (q *Queue) enqueueMessage(msg *msgqueue.Message) error {
	if q.noDelay && msg.Delay > 0 {
		msg.Delay = 0
	}
	msg.ReservedCount++

	if q.sync {
		return q.p.Process(msg)
	}
	return q.p.Add(msg)
}

func (q *Queue) isUniqueName(name string) bool {
	const redisPrefix = "memqueue"

	if name == "" {
		return true
	}
	key := fmt.Sprintf("%s:%s:%s", redisPrefix, q.opt.GroupName, name)
	exists := q.opt.Storage.Exists(key)
	return !exists
}

func (q *Queue) ReserveN(n int) ([]*msgqueue.Message, error) {
	return nil, internal.ErrNotSupported
}

func (q *Queue) Release(msg *msgqueue.Message) error {
	return q.enqueueMessage(msg)
}

func (q *Queue) Delete(msg *msgqueue.Message) error {
	return nil
}

func (q *Queue) DeleteBatch(msgs []*msgqueue.Message) error {
	if len(msgs) == 0 {
		return errors.New("msgqueue: no messages to delete")
	}
	for _, msg := range msgs {
		if err := q.Delete(msg); err != nil {
			return err
		}
	}
	return nil
}

func (q *Queue) Purge() error {
	return q.p.Purge()
}
<commit_msg>memqueue: fix sync option<commit_after>package memqueue

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/go-msgqueue/msgqueue"
	"github.com/go-msgqueue/msgqueue/internal"
)

var timers = sync.Pool{
	New: func() interface{} {
		t := time.NewTimer(time.Hour)
		t.Stop()
		return t
	},
}

type manager struct{}

func (manager) NewQueue(opt *msgqueue.Options) msgqueue.Queue {
	return NewQueue(opt)
}

func (manager) Queues() []msgqueue.Queue {
	var queues []msgqueue.Queue
	for _, q := range Queues() {
		queues = append(queues, q)
	}
	return queues
}

func NewManager() msgqueue.Manager {
	return manager{}
}

type Queue struct {
	opt *msgqueue.Options

	sync    bool
	noDelay bool

	p *msgqueue.Processor
}

var _ msgqueue.Queue = (*Queue)(nil)

func NewQueue(opt *msgqueue.Options) *Queue {
	opt.Init()

	q := Queue{
		opt: opt,
	}
	q.p = msgqueue.StartProcessor(&q, opt)

	registerQueue(&q)
	return &q
}

func (q *Queue) Name() string {
	return q.opt.Name
}

func (q *Queue) String() string {
	return fmt.Sprintf("Memqueue<Name=%s>", q.Name())
}

func (q *Queue) Options() *msgqueue.Options {
	return q.opt
}

func (q *Queue) Processor() *msgqueue.Processor {
	return q.p
}

func (q *Queue) SetSync(sync bool) {
	q.sync = sync
}

func (q *Queue) SetNoDelay(noDelay bool) {
	q.noDelay = noDelay
}

// Close is CloseTimeout with 30 seconds timeout.
func (q *Queue) Close() error {
	return q.CloseTimeout(30 * time.Second)
}

// Close closes the queue waiting for pending messages to be processed.
func (q *Queue) CloseTimeout(timeout time.Duration) error {
	defer unregisterQueue(q)
	return q.p.StopTimeout(timeout)
}

// Call creates a message using the args and adds it to the queue.
func (q *Queue) Call(args ...interface{}) error {
	msg := msgqueue.NewMessage(args...)
	return q.Add(msg)
}

// CallOnce works like Call, but it adds message with same args
// only once in a period.
func (q *Queue) CallOnce(period time.Duration, args ...interface{}) error {
	msg := msgqueue.NewMessage(args...)
	msg.SetDelayName(period, args...)
	return q.Add(msg)
}

// Add adds message to the queue.
func (q *Queue) Add(msg *msgqueue.Message) error {
	_, err := msg.GetBody()
	if err != nil {
		return err
	}

	if !q.isUniqueName(msg.Name) {
		return msgqueue.ErrDuplicate
	}
	return q.enqueueMessage(msg)
}

func (q *Queue) enqueueMessage(msg *msgqueue.Message) error {
	if (q.noDelay || q.sync) && msg.Delay > 0 {
		msg.Delay = 0
	}
	msg.ReservedCount++

	if q.sync {
		return q.p.Process(msg)
	}
	return q.p.Add(msg)
}

func (q *Queue) isUniqueName(name string) bool {
	const redisPrefix = "memqueue"

	if name == "" {
		return true
	}
	key := fmt.Sprintf("%s:%s:%s", redisPrefix, q.opt.GroupName, name)
	exists := q.opt.Storage.Exists(key)
	return !exists
}

func (q *Queue) ReserveN(n int) ([]*msgqueue.Message, error) {
	return nil, internal.ErrNotSupported
}

func (q *Queue) Release(msg *msgqueue.Message) error {
	return q.enqueueMessage(msg)
}

func (q *Queue) Delete(msg *msgqueue.Message) error {
	return nil
}

func (q *Queue) DeleteBatch(msgs []*msgqueue.Message) error {
	if len(msgs) == 0 {
		return errors.New("msgqueue: no messages to delete")
	}
	for _, msg := range msgs {
		if err := q.Delete(msg); err != nil {
			return err
		}
	}
	return nil
}

func (q *Queue) Purge() error {
	return q.p.Purge()
}
<|endoftext|>


<commit_before>/* Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

package go_kafka_client

import (
	"fmt"
	"sync"
	"time"
)

type messageBuffer struct {
	OutputChannel                  chan []*Message
	Messages                       []*Message
	Config                         *ConsumerConfig
	Timer                          *time.Timer
	MessageLock                    sync.Mutex
	Close                          chan bool
	stopSending                    bool
	TopicPartition                 TopicAndPartition
	askNextBatch                   chan TopicAndPartition
	disconnectChannelsForPartition chan TopicAndPartition
	flush                          chan bool
}

func newMessageBuffer(topicPartition TopicAndPartition, outputChannel chan []*Message, config *ConsumerConfig, askNextBatch chan TopicAndPartition, disconnectChannelsForPartition chan TopicAndPartition) *messageBuffer {
	buffer := &messageBuffer{
		OutputChannel:                  outputChannel,
		Messages:                       make([]*Message, 0),
		Config:                         config,
		Timer:                          time.NewTimer(config.FetchBatchTimeout),
		Close:                          make(chan bool),
		TopicPartition:                 topicPartition,
		askNextBatch:                   askNextBatch,
		disconnectChannelsForPartition: disconnectChannelsForPartition,
		flush: make(chan bool),
	}

	go buffer.autoFlush()
	go buffer.flushLoop()

	return buffer
}

func (mb *messageBuffer) String() string {
	return fmt.Sprintf("%s-MessageBuffer", &mb.TopicPartition)
}

func (mb *messageBuffer) autoFlush() {
	for {
		select {
		case <-mb.Close:
			return
		case <-mb.Timer.C:
			{
				Debug(mb, "Batch accumulation timed out. Flushing...")
				mb.Timer.Reset(mb.Config.FetchBatchTimeout)

				select {
				case mb.flush <- true:
				default:
				}
			}
		}
	}
}

func (mb *messageBuffer) flushLoop() {
	for _ = range mb.flush {
		if len(mb.Messages) > 0 {
			Debug(mb, "Flushing")
			mb.Timer.Reset(mb.Config.FetchBatchTimeout)
		flushLoop:
			for {
				select {
				case mb.OutputChannel <- mb.Messages:
					break flushLoop
				case <-time.After(200 * time.Millisecond):
					if mb.stopSending {
						return
					}
				}
			}
			Debug(mb, "Flushed")
			mb.Messages = make([]*Message, 0)
		}
	}
}

func (mb *messageBuffer) stop() {
	Debug(mb, "Stopping message buffer")
	mb.stopSending = true
	mb.Close <- true
	inLock(&mb.MessageLock, func() {
		close(mb.flush)
	})
	mb.disconnectChannelsForPartition <- mb.TopicPartition
	Debug(mb, "Stopped message buffer")
}

func (mb *messageBuffer) addBatch(data *TopicPartitionData) {
	inLock(&mb.MessageLock, func() {
		fetchResponseBlock := data.Data
		topicPartition := data.TopicPartition
		if topicPartition != mb.TopicPartition {
			panic(fmt.Sprintf("%s got batch for wrong topic and partition: %s", mb, topicPartition))
		}
		if fetchResponseBlock != nil {
			for _, message := range fetchResponseBlock.MsgSet.Messages {
				if message.Msg.Set != nil {
					for _, wrapped := range message.Msg.Set.Messages {
						mb.add(&Message{
							Key:       wrapped.Msg.Key,
							Value:     wrapped.Msg.Value,
							Topic:     topicPartition.Topic,
							Partition: topicPartition.Partition,
							Offset:    wrapped.Offset,
						})
					}
				} else {
					mb.add(&Message{
						Key:       message.Msg.Key,
						Value:     message.Msg.Value,
						Topic:     topicPartition.Topic,
						Partition: topicPartition.Partition,
						Offset:    message.Offset,
					})
				}
			}
		}
		mb.askNextBatch <- mb.TopicPartition
	})
}

func (mb *messageBuffer) add(msg *Message) {
	Debugf(mb, "Added message: %s", msg)
	mb.Messages = append(mb.Messages, msg)
	if len(mb.Messages) == mb.Config.FetchBatchSize {
		Debug(mb, "Batch is ready. Flushing")
		mb.flush <- true
	}
}
<commit_msg>message buffer bug fix<commit_after>/* Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

package go_kafka_client

import (
	"fmt"
	"sync"
	"time"
)

type messageBuffer struct {
	OutputChannel                  chan []*Message
	Messages                       []*Message
	Config                         *ConsumerConfig
	Timer                          *time.Timer
	MessageLock                    sync.Mutex
	Close                          chan bool
	stopSending                    bool
	TopicPartition                 TopicAndPartition
	askNextBatch                   chan TopicAndPartition
	disconnectChannelsForPartition chan TopicAndPartition
	flush                          chan bool
}

func newMessageBuffer(topicPartition TopicAndPartition, outputChannel chan []*Message, config *ConsumerConfig, askNextBatch chan TopicAndPartition, disconnectChannelsForPartition chan TopicAndPartition) *messageBuffer {
	buffer := &messageBuffer{
		OutputChannel:                  outputChannel,
		Messages:                       make([]*Message, 0),
		Config:                         config,
		Timer:                          time.NewTimer(config.FetchBatchTimeout),
		Close:                          make(chan bool),
		TopicPartition:                 topicPartition,
		askNextBatch:                   askNextBatch,
		disconnectChannelsForPartition: disconnectChannelsForPartition,
		flush: make(chan bool),
	}

	go buffer.autoFlush()
	go buffer.flushLoop()

	return buffer
}

func (mb *messageBuffer) String() string {
	return fmt.Sprintf("%s-MessageBuffer", &mb.TopicPartition)
}

func (mb *messageBuffer) autoFlush() {
	for {
		select {
		case <-mb.Close:
			return
		case <-mb.Timer.C:
			{
				Debug(mb, "Batch accumulation timed out. Flushing...")
				mb.Timer.Reset(mb.Config.FetchBatchTimeout)

				select {
				case mb.flush <- true:
				default:
				}
			}
		}
	}
}

func (mb *messageBuffer) flushLoop() {
	for _ = range mb.flush {
		if len(mb.Messages) > 0 {
			Debug(mb, "Flushing")
			mb.Timer.Reset(mb.Config.FetchBatchTimeout)
		flushLoop:
			for {
				select {
				case mb.OutputChannel <- mb.Messages:
					break flushLoop
				case <-time.After(200 * time.Millisecond):
					if mb.stopSending {
						return
					}
				}
			}
			Debug(mb, "Flushed")
			mb.Messages = make([]*Message, 0)
		}
	}
}

func (mb *messageBuffer) stop() {
	Debug(mb, "Stopping message buffer")
	mb.stopSending = true
	mb.Close <- true
	inLock(&mb.MessageLock, func() {
		close(mb.flush)
	})
	mb.disconnectChannelsForPartition <- mb.TopicPartition
	Debug(mb, "Stopped message buffer")
}

func (mb *messageBuffer) addBatch(data *TopicPartitionData) {
	inLock(&mb.MessageLock, func() {
		fetchResponseBlock := data.Data
		topicPartition := data.TopicPartition
		if topicPartition != mb.TopicPartition {
			panic(fmt.Sprintf("%s got batch for wrong topic and partition: %s", mb, topicPartition))
		}
		if fetchResponseBlock != nil {
			for _, message := range fetchResponseBlock.MsgSet.Messages {
				if message.Msg.Set != nil {
					for _, wrapped := range message.Msg.Set.Messages {
						mb.add(&Message{
							Key:       wrapped.Msg.Key,
							Value:     wrapped.Msg.Value,
							Topic:     topicPartition.Topic,
							Partition: topicPartition.Partition,
							Offset:    wrapped.Offset,
						})
					}
				} else {
					mb.add(&Message{
						Key:       message.Msg.Key,
						Value:     message.Msg.Value,
						Topic:     topicPartition.Topic,
						Partition: topicPartition.Partition,
						Offset:    message.Offset,
					})
				}
			}
		}

		askNextLoop:
		for {
			select {
			case mb.askNextBatch <- mb.TopicPartition:
				break askNextLoop
			case <-time.After(200 * time.Millisecond):
				if mb.stopSending {
					return
				}
			}
		}
	})
}

func (mb *messageBuffer) add(msg *Message) {
	Debugf(mb, "Added message: %s", msg)
	mb.Messages = append(mb.Messages, msg)
	if len(mb.Messages) == mb.Config.FetchBatchSize {
		Debug(mb, "Batch is ready. Flushing")
		mb.flush <- true
	}
}
<|endoftext|>


<commit_before>package metrics

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/mackerel"
	"github.com/mackerelio/mackerel-agent/util"
)

// pluginGenerator collects user-defined metrics.
// mackerel-agent runs specified command and parses the result for the metric names and values.
type pluginGenerator struct {
	Config config.PluginConfig
	Meta   *pluginMeta
}

// pluginMeta is generated from plugin command. (not the configuration file)
type pluginMeta struct {
	Graphs map[string]customGraphDef
}

type customGraphDef struct {
	Label   string
	Unit    string
	Metrics []customGraphMetricDef
}

type customGraphMetricDef struct {
	Name    string
	Label   string
	Stacked bool
}

var pluginLogger = logging.GetLogger("metrics.plugin")

const pluginPrefix = "custom."

var pluginConfigurationEnvName = "MACKEREL_AGENT_PLUGIN_META"

// NewPluginGenerator XXX
func NewPluginGenerator(conf config.PluginConfig) PluginGenerator {
	return &pluginGenerator{Config: conf}
}

func (g *pluginGenerator) Generate() (Values, error) {
	results, err := g.collectValues()
	if err != nil {
		return nil, err
	}
	return results, nil
}

func (g *pluginGenerator) PrepareGraphDefs() ([]mackerel.CreateGraphDefsPayload, error) {
	err := g.loadPluginMeta()
	if err != nil {
		return nil, err
	}

	payload := g.makeCreateGraphDefsPayload()
	return payload, nil
}

func (g *pluginGenerator) CustomIdentifier() *string {
	return g.Config.CustomIdentifier
}

// loadPluginMeta obtains plugin information (e.g. graph visuals, metric
// namespaces, etc) from the command specified.
// mackerel-agent runs the command with MACKEREL_AGENT_PLUGIN_META
// environment variable set.  The command is supposed to output like below:
//
// 	# mackerel-agent-plugin
// 	{
// 	  "graphs": {
// 	    GRAPH_NAME: {
// 	      "label": GRAPH_LABEL,
// 	      "unit": UNIT_TYPE
// 	      "metrics": [
// 	        {
// 	          "name": METRIC_NAME,
// 	          "label": METRIC_LABEL
// 	        },
// 	        ...
// 	      ]
// 	    },
// 	    GRAPH_NAME: ...
// 	  }
// 	}
//
// Valid UNIT_TYPEs are: "float", "integer", "percentage", "bytes", "bytes/sec", "iops"
//
// The output should start with a line beginning with '#', which contains
// meta-info of the configuration. (eg. plugin schema version)
//
// Below is a working example where the plugin emits metrics named "dice.d6" and "dice.d20":
//
// 	{
// 	  "graphs": {
// 	    "dice": {
// 	      "metrics": [
// 	        {
// 	          "name": "d6",
// 	          "label": "Die (d6)"
// 	        },
// 	        {
// 	          "name": "d20",
// 	          "label": "Die (d20)"
// 	        }
// 	      ],
// 	      "unit": "integer",
// 	      "label": "My Dice"
// 	    }
// 	  }
// 	}
func (g *pluginGenerator) loadPluginMeta() error {
	command := g.Config.Command
	pluginLogger.Debugf("Obtaining plugin configuration: %q", command)

	// Set environment variable to make the plugin command generate its configuration
	os.Setenv(pluginConfigurationEnvName, "1")
	defer os.Setenv(pluginConfigurationEnvName, "")

	stdout, stderr, exitCode, err := util.RunCommand(command)
	if err != nil {
		return fmt.Errorf("running %q failed: %s, exit=%d stderr=%q", command, err, exitCode, stderr)
	}

	outBuffer := bufio.NewReader(strings.NewReader(stdout))
	// Read the plugin configuration meta (version etc)

	headerLine, err := outBuffer.ReadString('\n')
	if err != nil {
		return fmt.Errorf("while reading the first line of command %q: %s", command, err)
	}

	// Parse the header line of format:
	// # mackerel-agent-plugin [key=value]...
	pluginMetaHeader := map[string]string{}

	re := regexp.MustCompile(`^#\s*mackerel-agent-plugin\b(.*)`)
	m := re.FindStringSubmatch(headerLine)
	if m == nil {
		return fmt.Errorf("bad format of first line: %q", headerLine)
	}

	for _, field := range strings.Fields(m[1]) {
		keyValue := strings.Split(field, "=")
		var value string
		if len(keyValue) > 1 {
			value = keyValue[1]
		} else {
			value = ""
		}
		pluginMetaHeader[keyValue[0]] = value
	}

	// Check schema version
	version, ok := pluginMetaHeader["version"]
	if !ok {
		version = "1"
	}

	if version != "1" {
		return fmt.Errorf("unsupported plugin meta version: %q", version)
	}

	conf := &pluginMeta{}
	err = json.NewDecoder(outBuffer).Decode(conf)

	if err != nil {
		return fmt.Errorf("while reading plugin configuration: %s", err)
	}

	g.Meta = conf

	return nil
}

func (g *pluginGenerator) makeCreateGraphDefsPayload() []mackerel.CreateGraphDefsPayload {
	if g.Meta == nil {
		return nil
	}

	payloads := []mackerel.CreateGraphDefsPayload{}

	for key, graph := range g.Meta.Graphs {
		payload := mackerel.CreateGraphDefsPayload{
			Name:        pluginPrefix + key,
			DisplayName: graph.Label,
			Unit:        graph.Unit,
		}
		if payload.Unit == "" {
			payload.Unit = "float"
		}

		for _, metric := range graph.Metrics {
			metricPayload := mackerel.CreateGraphDefsPayloadMetric{
				Name:        pluginPrefix + key + "." + metric.Name,
				DisplayName: metric.Label,
				IsStacked:   metric.Stacked,
			}
			payload.Metrics = append(payload.Metrics, metricPayload)
		}

		payloads = append(payloads, payload)
	}

	return payloads
}

var delimReg = regexp.MustCompile(`[\s\t]+`)

func (g *pluginGenerator) collectValues() (Values, error) {
	command := g.Config.Command
	pluginLogger.Debugf("Executing plugin: command = \"%s\"", command)

	os.Setenv(pluginConfigurationEnvName, "")
	stdout, stderr, _, err := util.RunCommand(command)

	if stderr != "" {
		pluginLogger.Infof("command %q outputted to STDERR: %q", stderr)
	}
	if err != nil {
		pluginLogger.Errorf("Failed to execute command %q (skip these metrics):\n", command)
		return nil, err
	}

	results := make(map[string]float64, 0)
	for _, line := range strings.Split(stdout, "\n") {
		// Key, value, timestamp
		// ex.) tcp.CLOSING 0 1397031808
		items := delimReg.Split(line, 3)
		if len(items) != 3 {
			continue
		}
		value, err := strconv.ParseFloat(items[1], 64)
		if err != nil {
			pluginLogger.Warningf("Failed to parse values: %s", err)
			continue
		}

		key := items[0]

		results[pluginPrefix+key] = value
	}

	return results, nil
}
<commit_msg>fix %!q(MISSING) in log<commit_after>package metrics

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/mackerel"
	"github.com/mackerelio/mackerel-agent/util"
)

// pluginGenerator collects user-defined metrics.
// mackerel-agent runs specified command and parses the result for the metric names and values.
type pluginGenerator struct {
	Config config.PluginConfig
	Meta   *pluginMeta
}

// pluginMeta is generated from plugin command. (not the configuration file)
type pluginMeta struct {
	Graphs map[string]customGraphDef
}

type customGraphDef struct {
	Label   string
	Unit    string
	Metrics []customGraphMetricDef
}

type customGraphMetricDef struct {
	Name    string
	Label   string
	Stacked bool
}

var pluginLogger = logging.GetLogger("metrics.plugin")

const pluginPrefix = "custom."

var pluginConfigurationEnvName = "MACKEREL_AGENT_PLUGIN_META"

// NewPluginGenerator XXX
func NewPluginGenerator(conf config.PluginConfig) PluginGenerator {
	return &pluginGenerator{Config: conf}
}

func (g *pluginGenerator) Generate() (Values, error) {
	results, err := g.collectValues()
	if err != nil {
		return nil, err
	}
	return results, nil
}

func (g *pluginGenerator) PrepareGraphDefs() ([]mackerel.CreateGraphDefsPayload, error) {
	err := g.loadPluginMeta()
	if err != nil {
		return nil, err
	}

	payload := g.makeCreateGraphDefsPayload()
	return payload, nil
}

func (g *pluginGenerator) CustomIdentifier() *string {
	return g.Config.CustomIdentifier
}

// loadPluginMeta obtains plugin information (e.g. graph visuals, metric
// namespaces, etc) from the command specified.
// mackerel-agent runs the command with MACKEREL_AGENT_PLUGIN_META
// environment variable set.  The command is supposed to output like below:
//
// 	# mackerel-agent-plugin
// 	{
// 	  "graphs": {
// 	    GRAPH_NAME: {
// 	      "label": GRAPH_LABEL,
// 	      "unit": UNIT_TYPE
// 	      "metrics": [
// 	        {
// 	          "name": METRIC_NAME,
// 	          "label": METRIC_LABEL
// 	        },
// 	        ...
// 	      ]
// 	    },
// 	    GRAPH_NAME: ...
// 	  }
// 	}
//
// Valid UNIT_TYPEs are: "float", "integer", "percentage", "bytes", "bytes/sec", "iops"
//
// The output should start with a line beginning with '#', which contains
// meta-info of the configuration. (eg. plugin schema version)
//
// Below is a working example where the plugin emits metrics named "dice.d6" and "dice.d20":
//
// 	{
// 	  "graphs": {
// 	    "dice": {
// 	      "metrics": [
// 	        {
// 	          "name": "d6",
// 	          "label": "Die (d6)"
// 	        },
// 	        {
// 	          "name": "d20",
// 	          "label": "Die (d20)"
// 	        }
// 	      ],
// 	      "unit": "integer",
// 	      "label": "My Dice"
// 	    }
// 	  }
// 	}
func (g *pluginGenerator) loadPluginMeta() error {
	command := g.Config.Command
	pluginLogger.Debugf("Obtaining plugin configuration: %q", command)

	// Set environment variable to make the plugin command generate its configuration
	os.Setenv(pluginConfigurationEnvName, "1")
	defer os.Setenv(pluginConfigurationEnvName, "")

	stdout, stderr, exitCode, err := util.RunCommand(command)
	if err != nil {
		return fmt.Errorf("running %q failed: %s, exit=%d stderr=%q", command, err, exitCode, stderr)
	}

	outBuffer := bufio.NewReader(strings.NewReader(stdout))
	// Read the plugin configuration meta (version etc)

	headerLine, err := outBuffer.ReadString('\n')
	if err != nil {
		return fmt.Errorf("while reading the first line of command %q: %s", command, err)
	}

	// Parse the header line of format:
	// # mackerel-agent-plugin [key=value]...
	pluginMetaHeader := map[string]string{}

	re := regexp.MustCompile(`^#\s*mackerel-agent-plugin\b(.*)`)
	m := re.FindStringSubmatch(headerLine)
	if m == nil {
		return fmt.Errorf("bad format of first line: %q", headerLine)
	}

	for _, field := range strings.Fields(m[1]) {
		keyValue := strings.Split(field, "=")
		var value string
		if len(keyValue) > 1 {
			value = keyValue[1]
		} else {
			value = ""
		}
		pluginMetaHeader[keyValue[0]] = value
	}

	// Check schema version
	version, ok := pluginMetaHeader["version"]
	if !ok {
		version = "1"
	}

	if version != "1" {
		return fmt.Errorf("unsupported plugin meta version: %q", version)
	}

	conf := &pluginMeta{}
	err = json.NewDecoder(outBuffer).Decode(conf)

	if err != nil {
		return fmt.Errorf("while reading plugin configuration: %s", err)
	}

	g.Meta = conf

	return nil
}

func (g *pluginGenerator) makeCreateGraphDefsPayload() []mackerel.CreateGraphDefsPayload {
	if g.Meta == nil {
		return nil
	}

	payloads := []mackerel.CreateGraphDefsPayload{}

	for key, graph := range g.Meta.Graphs {
		payload := mackerel.CreateGraphDefsPayload{
			Name:        pluginPrefix + key,
			DisplayName: graph.Label,
			Unit:        graph.Unit,
		}
		if payload.Unit == "" {
			payload.Unit = "float"
		}

		for _, metric := range graph.Metrics {
			metricPayload := mackerel.CreateGraphDefsPayloadMetric{
				Name:        pluginPrefix + key + "." + metric.Name,
				DisplayName: metric.Label,
				IsStacked:   metric.Stacked,
			}
			payload.Metrics = append(payload.Metrics, metricPayload)
		}

		payloads = append(payloads, payload)
	}

	return payloads
}

var delimReg = regexp.MustCompile(`[\s\t]+`)

func (g *pluginGenerator) collectValues() (Values, error) {
	command := g.Config.Command
	pluginLogger.Debugf("Executing plugin: command = \"%s\"", command)

	os.Setenv(pluginConfigurationEnvName, "")
	stdout, stderr, _, err := util.RunCommand(command)

	if stderr != "" {
		pluginLogger.Infof("command %q outputted to STDERR: %q", command, stderr)
	}
	if err != nil {
		pluginLogger.Errorf("Failed to execute command %q (skip these metrics):\n", command)
		return nil, err
	}

	results := make(map[string]float64, 0)
	for _, line := range strings.Split(stdout, "\n") {
		// Key, value, timestamp
		// ex.) tcp.CLOSING 0 1397031808
		items := delimReg.Split(line, 3)
		if len(items) != 3 {
			continue
		}
		value, err := strconv.ParseFloat(items[1], 64)
		if err != nil {
			pluginLogger.Warningf("Failed to parse values: %s", err)
			continue
		}

		key := items[0]

		results[pluginPrefix+key] = value
	}

	return results, nil
}
<|endoftext|>


<commit_before>package main

import (
	"configuration"
	"coordinator"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"server"
	"strconv"
	"time"

	log "code.google.com/p/log4go"
)

func setupLogging(loggingLevel, logFile string) {
	level := log.DEBUG
	switch loggingLevel {
	case "info":
		level = log.INFO
	case "warn":
		level = log.WARNING
	case "error":
		level = log.ERROR
	}

	for _, filter := range log.Global {
		filter.Level = level
	}

	if logFile == "stdout" {
		flw := log.NewConsoleLogWriter()
		log.AddFilter("stdout", level, flw)

	} else {
		logFileDir := filepath.Dir(logFile)
		os.MkdirAll(logFileDir, 0744)

		flw := log.NewFileLogWriter(logFile, false)
		log.AddFilter("file", level, flw)

		flw.SetFormat("[%D %T] [%L] (%S) %M")
		flw.SetRotate(true)
		flw.SetRotateSize(0)
		flw.SetRotateLines(0)
		flw.SetRotateDaily(true)
	}

	log.Info("Redirectoring logging to %s", logFile)
}

func main() {
	fileName := flag.String("config", "config.toml.sample", "Config file")
	wantsVersion := flag.Bool("v", false, "Get version number")
	resetRootPassword := flag.Bool("reset-root", false, "Reset root password")
	pidFile := flag.String("pidfile", "", "the pid file")

	runtime.GOMAXPROCS(runtime.NumCPU())
	flag.Parse()

	if wantsVersion != nil && *wantsVersion {
		fmt.Printf("InfluxDB v%s (git: %s)\n", version, gitSha)
		return
	}
	config := configuration.LoadConfiguration(*fileName)
	setupLogging(config.LogLevel, config.LogFile)

	if pidFile != nil && *pidFile != "" {
		pid := strconv.Itoa(os.Getpid())
		if err := ioutil.WriteFile(*pidFile, []byte(pid), 0644); err != nil {
			panic(err)
		}
	}

	if config.BindAddress == "" {
		log.Info("Starting Influx Server...")
	} else {
		log.Info("Starting Influx Server bound to %s ...", config.BindAddress)
	}
	log.Info(`
+---------------------------------------------+
|  _____        __ _            _____  ____   |
| |_   _|      / _| |          |  __ \|  _ \  |
|   | |  _ __ | |_| |_   ___  _| |  | | |_) | |
|   | | | '_ \|  _| | | | \ \/ / |  | |  _ <  |
|  _| |_| | | | | | | |_| |>  <| |__| | |_) | |
| |_____|_| |_|_| |_|\__,_/_/\_\_____/|____/  |
+---------------------------------------------+

`)
	os.MkdirAll(config.RaftDir, 0744)
	os.MkdirAll(config.DataDir, 0744)
	server, err := server.NewServer(config)
	if err != nil {
		// sleep for the log to flush
		time.Sleep(time.Second)
		panic(err)
	}

	if err := startProfiler(server); err != nil {
		panic(err)
	}

	if *resetRootPassword {
		// TODO: make this not suck
		// This is ghetto as hell, but it'll work for now.
		go func() {
			time.Sleep(2 * time.Second) // wait for the raft server to join the cluster

			log.Warn("Resetting root's password to %s", coordinator.DEFAULT_ROOT_PWD)
			if err := server.RaftServer.CreateRootUser(); err != nil {
				panic(err)
			}
		}()
	}
	err = server.ListenAndServe()
	if err != nil {
		log.Error("ListenAndServe failed: ", err)
	}
}
<commit_msg>print the version of leveldb when -v is passed to influxd<commit_after>package main

import (
	"configuration"
	"coordinator"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"server"
	"strconv"
	"time"

	"github.com/jmhodges/levigo"

	log "code.google.com/p/log4go"
)

func setupLogging(loggingLevel, logFile string) {
	level := log.DEBUG
	switch loggingLevel {
	case "info":
		level = log.INFO
	case "warn":
		level = log.WARNING
	case "error":
		level = log.ERROR
	}

	for _, filter := range log.Global {
		filter.Level = level
	}

	if logFile == "stdout" {
		flw := log.NewConsoleLogWriter()
		log.AddFilter("stdout", level, flw)

	} else {
		logFileDir := filepath.Dir(logFile)
		os.MkdirAll(logFileDir, 0744)

		flw := log.NewFileLogWriter(logFile, false)
		log.AddFilter("file", level, flw)

		flw.SetFormat("[%D %T] [%L] (%S) %M")
		flw.SetRotate(true)
		flw.SetRotateSize(0)
		flw.SetRotateLines(0)
		flw.SetRotateDaily(true)
	}

	log.Info("Redirectoring logging to %s", logFile)
}

func main() {
	fileName := flag.String("config", "config.toml.sample", "Config file")
	wantsVersion := flag.Bool("v", false, "Get version number")
	resetRootPassword := flag.Bool("reset-root", false, "Reset root password")
	pidFile := flag.String("pidfile", "", "the pid file")

	runtime.GOMAXPROCS(runtime.NumCPU())
	flag.Parse()

	if wantsVersion != nil && *wantsVersion {
		fmt.Printf("InfluxDB v%s (git: %s) (leveldb: %d.%d)\n", version, gitSha, levigo.GetLevelDBMajorVersion(), levigo.GetLevelDBMinorVersion())
		return
	}
	config := configuration.LoadConfiguration(*fileName)
	setupLogging(config.LogLevel, config.LogFile)

	if pidFile != nil && *pidFile != "" {
		pid := strconv.Itoa(os.Getpid())
		if err := ioutil.WriteFile(*pidFile, []byte(pid), 0644); err != nil {
			panic(err)
		}
	}

	if config.BindAddress == "" {
		log.Info("Starting Influx Server...")
	} else {
		log.Info("Starting Influx Server bound to %s ...", config.BindAddress)
	}
	log.Info(`
+---------------------------------------------+
|  _____        __ _            _____  ____   |
| |_   _|      / _| |          |  __ \|  _ \  |
|   | |  _ __ | |_| |_   ___  _| |  | | |_) | |
|   | | | '_ \|  _| | | | \ \/ / |  | |  _ <  |
|  _| |_| | | | | | | |_| |>  <| |__| | |_) | |
| |_____|_| |_|_| |_|\__,_/_/\_\_____/|____/  |
+---------------------------------------------+

`)
	os.MkdirAll(config.RaftDir, 0744)
	os.MkdirAll(config.DataDir, 0744)
	server, err := server.NewServer(config)
	if err != nil {
		// sleep for the log to flush
		time.Sleep(time.Second)
		panic(err)
	}

	if err := startProfiler(server); err != nil {
		panic(err)
	}

	if *resetRootPassword {
		// TODO: make this not suck
		// This is ghetto as hell, but it'll work for now.
		go func() {
			time.Sleep(2 * time.Second) // wait for the raft server to join the cluster

			log.Warn("Resetting root's password to %s", coordinator.DEFAULT_ROOT_PWD)
			if err := server.RaftServer.CreateRootUser(); err != nil {
				panic(err)
			}
		}()
	}
	err = server.ListenAndServe()
	if err != nil {
		log.Error("ListenAndServe failed: ", err)
	}
}
<|endoftext|>


<commit_before>package curator

import (
	"fmt"

	"github.com/golang/glog"
	"github.com/samuel/go-zookeeper/zk"
)

type CreateBuilder interface {
	// PathAndBytesable
	//
	// Commit the currently building operation using the given path
	ForPath(path string) (string, error)

	// Commit the currently building operation using the given path and data
	ForPathWithData(path string, payload []byte) (string, error)

	// Causes any parent nodes to get created if they haven't already been
	CreatingParentsIfNeeded() CreateBuilder

	// CreateModable[T]
	//
	// Set a create mode - the default is CreateMode.PERSISTENT
	WithMode(mode CreateMode) CreateBuilder

	// ACLable[T]
	//
	// Set an ACL list
	WithACL(acl ...zk.ACL) CreateBuilder

	// Compressible[T]
	//
	// Cause the data to be compressed using the configured compression provider
	Compressed() CreateBuilder

	// Backgroundable[T]
	//
	// Perform the action in the background
	InBackground() CreateBuilder

	// Perform the action in the background
	InBackgroundWithContext(context interface{}) CreateBuilder

	// Perform the action in the background
	InBackgroundWithCallback(callback BackgroundCallback) CreateBuilder

	// Perform the action in the background
	InBackgroundWithCallbackAndContext(callback BackgroundCallback, context interface{}) CreateBuilder
}

type CheckExistsBuilder interface {
	// Pathable[T]
	//
	// Commit the currently building operation using the given path
	ForPath(path string) (*zk.Stat, error)

	// Watchable[T]
	//
	// Have the operation set a watch
	Watched() CheckExistsBuilder

	// Set a watcher for the operation
	UsingWatcher(watcher Watcher) CheckExistsBuilder

	// Backgroundable[T]
	//
	// Perform the action in the background
	InBackground() CheckExistsBuilder

	// Perform the action in the background
	InBackgroundWithContext(context interface{}) CheckExistsBuilder

	// Perform the action in the background
	InBackgroundWithCallback(callback BackgroundCallback) CheckExistsBuilder

	// Perform the action in the background
	InBackgroundWithCallbackAndContext(callback BackgroundCallback, context interface{}) CheckExistsBuilder
}

type DeleteBuilder interface {
}

type GetDataBuilder interface {
}

type SetDataBuilder interface {
}

type GetChildrenBuilder interface {
}

type GetACLBuilder interface {
}

type SetACLBuilder interface {
}

type TransactionCreateBuilder interface {
}

type TransactionDeleteBuilder interface {
}

type TransactionSetDataBuilder interface {
}

type TransactionCheckBuilder interface {
}

type acling struct {
	aclList     []zk.ACL
	aclProvider ACLProvider
}

type backgrounding struct {
	inBackground bool
	context      interface{}
	callback     BackgroundCallback
}

type watching struct {
	watcher Watcher
	watched bool
}

type createBuilder struct {
	client                *curatorFramework
	createMode            CreateMode
	backgrounding         backgrounding
	createParentsIfNeeded bool
	compress              bool
	acling                acling
}

func (b *createBuilder) ForPath(path string) (string, error) {
	return b.ForPathWithData(path, b.client.defaultData)
}

func (b *createBuilder) ForPathWithData(givenPath string, payload []byte) (string, error) {
	if b.compress {
		if data, err := b.client.compressionProvider.Compress(givenPath, payload); err != nil {
			return "", err
		} else {
			payload = data
		}
	}

	adjustedPath := b.client.fixForNamespace(givenPath, b.createMode.IsSequential())

	if b.backgrounding.inBackground {
		go b.pathInBackground(adjustedPath, payload, givenPath)

		return b.client.unfixForNamespace(adjustedPath), nil
	} else {
		path, err := b.pathInForeground(adjustedPath, payload)

		return b.client.unfixForNamespace(path), err
	}
}

func (b *createBuilder) pathInBackground(path string, payload []byte, givenPath string) {
	tracer := b.client.ZookeeperClient().startTracer("createBuilder.pathInBackground")

	defer tracer.Commit()

	createdPath, err := b.pathInForeground(path, payload)

	event := &curatorEvent{
		eventType: CREATE,
		err:       err,
		path:      createdPath,
		data:      payload,
		context:   b.backgrounding.context,
	}

	if err != nil {
		event.path = givenPath
	} else {
		event.name = GetNodeFromPath(createdPath)
	}

	if b.backgrounding.callback != nil {
		b.backgrounding.callback.ProcessResult(b.client, event)
	} else if glog.V(3) {
		glog.V(3).Infof("ignore CREATE event: %s", event)
	}
}

func (b *createBuilder) pathInForeground(path string, payload []byte) (string, error) {
	zkClient := b.client.ZookeeperClient()

	result, err := zkClient.newRetryLoop().callWithRetry(func() (result interface{}, err error) {
		result, err = zkClient.Conn().Create(path, payload, int32(b.createMode), b.acling.aclList)

		if err == zk.ErrNoNode && b.createParentsIfNeeded {
			MakeDirs(zkClient.Conn(), path, false, b.acling.aclProvider)

			result, err = zkClient.Conn().Create(path, payload, int32(b.createMode), b.acling.aclList)
		}

		return
	})

	if err != nil {
		return "", err
	} else if createdPath, ok := result.(string); !ok {
		return "", fmt.Errorf("fail to convert result to string, %s", result)
	} else {
		return createdPath, nil
	}
}

func (b *createBuilder) CreatingParentsIfNeeded() CreateBuilder {
	b.createParentsIfNeeded = true

	return b
}

func (b *createBuilder) WithMode(mode CreateMode) CreateBuilder {
	b.createMode = mode

	return b
}

func (b *createBuilder) WithACL(acls ...zk.ACL) CreateBuilder {
	b.acling = acling{aclList: acls, aclProvider: b.client.aclProvider}

	return b
}

func (b *createBuilder) Compressed() CreateBuilder {
	b.compress = true

	return b
}

func (b *createBuilder) InBackground() CreateBuilder {
	b.backgrounding = backgrounding{inBackground: true}

	return b
}

func (b *createBuilder) InBackgroundWithContext(context interface{}) CreateBuilder {
	b.backgrounding = backgrounding{inBackground: true, context: context}

	return b
}

func (b *createBuilder) InBackgroundWithCallback(callback BackgroundCallback) CreateBuilder {
	b.backgrounding = backgrounding{inBackground: true, callback: callback}

	return b
}

func (b *createBuilder) InBackgroundWithCallbackAndContext(callback BackgroundCallback, context interface{}) CreateBuilder {
	b.backgrounding = backgrounding{inBackground: true, context: context, callback: callback}

	return b
}

type checkExistsBuilder struct {
	client        *curatorFramework
	backgrounding backgrounding
	watching      watching
}

func (b *checkExistsBuilder) ForPath(givenPath string) (*zk.Stat, error) {
	adjustedPath := b.client.fixForNamespace(givenPath, false)

	if b.backgrounding.inBackground {
		go b.pathInBackground(adjustedPath)

		return nil, nil
	} else {
		return b.pathInForeground(adjustedPath)
	}
}

func (b *checkExistsBuilder) pathInBackground(path string) {
	tracer := b.client.ZookeeperClient().startTracer("checkExistsBuilder.pathInBackground")

	defer tracer.Commit()

	stat, err := b.pathInForeground(path)

	event := &curatorEvent{
		eventType: EXISTS,
		err:       err,
		path:      path,
		stat:      stat,
		context:   b.backgrounding.context,
	}

	if b.backgrounding.callback != nil {
		b.backgrounding.callback.ProcessResult(b.client, event)
	} else if glog.V(3) {
		glog.V(3).Infof("ignore EXISTS event: %s", event)
	}
}

func (b *checkExistsBuilder) pathInForeground(path string) (*zk.Stat, error) {
	zkClient := b.client.ZookeeperClient()

	result, err := zkClient.newRetryLoop().callWithRetry(func() (interface{}, error) {
		var exists bool
		var stat *zk.Stat
		var events <-chan zk.Event
		var err error

		if b.watching.watched || b.watching.watcher != nil {
			exists, stat, events, err = zkClient.Conn().ExistsW(path)
		} else {
			exists, stat, err = zkClient.Conn().Exists(path)
		}

		if err != nil {
			return nil, err
		} else if !exists {
			return stat, zk.ErrNoNode
		} else {
			return stat, nil
		}
	})

	stat, _ := result.(*zk.Stat)

	return stat, err
}

func (b *checkExistsBuilder) Watched() CheckExistsBuilder {
	b.watching.watched = true

	return b
}

func (b *checkExistsBuilder) UsingWatcher(watcher Watcher) CheckExistsBuilder {
	b.watching.watcher = b.client.getNamespaceWatcher(watcher)

	return b
}

func (b *checkExistsBuilder) InBackground() CheckExistsBuilder {
	b.backgrounding = backgrounding{inBackground: true}

	return b
}

func (b *checkExistsBuilder) InBackgroundWithContext(context interface{}) CheckExistsBuilder {
	b.backgrounding = backgrounding{inBackground: true, context: context}

	return b
}

func (b *checkExistsBuilder) InBackgroundWithCallback(callback BackgroundCallback) CheckExistsBuilder {
	b.backgrounding = backgrounding{inBackground: true, callback: callback}

	return b
}

func (b *checkExistsBuilder) InBackgroundWithCallbackAndContext(callback BackgroundCallback, context interface{}) CheckExistsBuilder {
	b.backgrounding = backgrounding{inBackground: true, context: context, callback: callback}

	return b
}
<commit_msg>handle conn and events<commit_after>package curator

import (
	"fmt"

	"github.com/golang/glog"
	"github.com/samuel/go-zookeeper/zk"
)

type CreateBuilder interface {
	// PathAndBytesable
	//
	// Commit the currently building operation using the given path
	ForPath(path string) (string, error)

	// Commit the currently building operation using the given path and data
	ForPathWithData(path string, payload []byte) (string, error)

	// Causes any parent nodes to get created if they haven't already been
	CreatingParentsIfNeeded() CreateBuilder

	// CreateModable[T]
	//
	// Set a create mode - the default is CreateMode.PERSISTENT
	WithMode(mode CreateMode) CreateBuilder

	// ACLable[T]
	//
	// Set an ACL list
	WithACL(acl ...zk.ACL) CreateBuilder

	// Compressible[T]
	//
	// Cause the data to be compressed using the configured compression provider
	Compressed() CreateBuilder

	// Backgroundable[T]
	//
	// Perform the action in the background
	InBackground() CreateBuilder

	// Perform the action in the background
	InBackgroundWithContext(context interface{}) CreateBuilder

	// Perform the action in the background
	InBackgroundWithCallback(callback BackgroundCallback) CreateBuilder

	// Perform the action in the background
	InBackgroundWithCallbackAndContext(callback BackgroundCallback, context interface{}) CreateBuilder
}

type CheckExistsBuilder interface {
	// Pathable[T]
	//
	// Commit the currently building operation using the given path
	ForPath(path string) (*zk.Stat, error)

	// Watchable[T]
	//
	// Have the operation set a watch
	Watched() CheckExistsBuilder

	// Set a watcher for the operation
	UsingWatcher(watcher Watcher) CheckExistsBuilder

	// Backgroundable[T]
	//
	// Perform the action in the background
	InBackground() CheckExistsBuilder

	// Perform the action in the background
	InBackgroundWithContext(context interface{}) CheckExistsBuilder

	// Perform the action in the background
	InBackgroundWithCallback(callback BackgroundCallback) CheckExistsBuilder

	// Perform the action in the background
	InBackgroundWithCallbackAndContext(callback BackgroundCallback, context interface{}) CheckExistsBuilder
}

type DeleteBuilder interface {
}

type GetDataBuilder interface {
}

type SetDataBuilder interface {
}

type GetChildrenBuilder interface {
}

type GetACLBuilder interface {
}

type SetACLBuilder interface {
}

type TransactionCreateBuilder interface {
}

type TransactionDeleteBuilder interface {
}

type TransactionSetDataBuilder interface {
}

type TransactionCheckBuilder interface {
}

type acling struct {
	aclList     []zk.ACL
	aclProvider ACLProvider
}

type backgrounding struct {
	inBackground bool
	context      interface{}
	callback     BackgroundCallback
}

type watching struct {
	watcher Watcher
	watched bool
}

type createBuilder struct {
	client                *curatorFramework
	createMode            CreateMode
	backgrounding         backgrounding
	createParentsIfNeeded bool
	compress              bool
	acling                acling
}

func (b *createBuilder) ForPath(path string) (string, error) {
	return b.ForPathWithData(path, b.client.defaultData)
}

func (b *createBuilder) ForPathWithData(givenPath string, payload []byte) (string, error) {
	if b.compress {
		if data, err := b.client.compressionProvider.Compress(givenPath, payload); err != nil {
			return "", err
		} else {
			payload = data
		}
	}

	adjustedPath := b.client.fixForNamespace(givenPath, b.createMode.IsSequential())

	if b.backgrounding.inBackground {
		go b.pathInBackground(adjustedPath, payload, givenPath)

		return b.client.unfixForNamespace(adjustedPath), nil
	} else {
		path, err := b.pathInForeground(adjustedPath, payload)

		return b.client.unfixForNamespace(path), err
	}
}

func (b *createBuilder) pathInBackground(path string, payload []byte, givenPath string) {
	tracer := b.client.ZookeeperClient().startTracer("createBuilder.pathInBackground")

	defer tracer.Commit()

	createdPath, err := b.pathInForeground(path, payload)

	event := &curatorEvent{
		eventType: CREATE,
		err:       err,
		path:      createdPath,
		data:      payload,
		context:   b.backgrounding.context,
	}

	if err != nil {
		event.path = givenPath
	} else {
		event.name = GetNodeFromPath(createdPath)
	}

	if b.backgrounding.callback != nil {
		b.backgrounding.callback(b.client, event)
	} else if glog.V(3) {
		glog.V(3).Infof("ignore CREATE event: %s", event)
	}
}

func (b *createBuilder) pathInForeground(path string, payload []byte) (string, error) {
	zkClient := b.client.ZookeeperClient()

	result, err := zkClient.newRetryLoop().callWithRetry(func() (interface{}, error) {
		if conn, err := zkClient.Conn(); err != nil {
			return nil, err
		} else {
			result, err := conn.Create(path, payload, int32(b.createMode), b.acling.aclList)

			if err == zk.ErrNoNode && b.createParentsIfNeeded {
				MakeDirs(conn, path, false, b.acling.aclProvider)

				return conn.Create(path, payload, int32(b.createMode), b.acling.aclList)
			} else {
				return result, err
			}
		}
	})

	if err != nil {
		return "", err
	} else if createdPath, ok := result.(string); !ok {
		return "", fmt.Errorf("fail to convert result to string, %s", result)
	} else {
		return createdPath, nil
	}
}

func (b *createBuilder) CreatingParentsIfNeeded() CreateBuilder {
	b.createParentsIfNeeded = true

	return b
}

func (b *createBuilder) WithMode(mode CreateMode) CreateBuilder {
	b.createMode = mode

	return b
}

func (b *createBuilder) WithACL(acls ...zk.ACL) CreateBuilder {
	b.acling = acling{aclList: acls, aclProvider: b.client.aclProvider}

	return b
}

func (b *createBuilder) Compressed() CreateBuilder {
	b.compress = true

	return b
}

func (b *createBuilder) InBackground() CreateBuilder {
	b.backgrounding = backgrounding{inBackground: true}

	return b
}

func (b *createBuilder) InBackgroundWithContext(context interface{}) CreateBuilder {
	b.backgrounding = backgrounding{inBackground: true, context: context}

	return b
}

func (b *createBuilder) InBackgroundWithCallback(callback BackgroundCallback) CreateBuilder {
	b.backgrounding = backgrounding{inBackground: true, callback: callback}

	return b
}

func (b *createBuilder) InBackgroundWithCallbackAndContext(callback BackgroundCallback, context interface{}) CreateBuilder {
	b.backgrounding = backgrounding{inBackground: true, context: context, callback: callback}

	return b
}

type checkExistsBuilder struct {
	client        *curatorFramework
	backgrounding backgrounding
	watching      watching
}

func (b *checkExistsBuilder) ForPath(givenPath string) (*zk.Stat, error) {
	adjustedPath := b.client.fixForNamespace(givenPath, false)

	if b.backgrounding.inBackground {
		go b.pathInBackground(adjustedPath)

		return nil, nil
	} else {
		return b.pathInForeground(adjustedPath)
	}
}

func (b *checkExistsBuilder) pathInBackground(path string) {
	tracer := b.client.ZookeeperClient().startTracer("checkExistsBuilder.pathInBackground")

	defer tracer.Commit()

	stat, err := b.pathInForeground(path)

	event := &curatorEvent{
		eventType: EXISTS,
		err:       err,
		path:      path,
		stat:      stat,
		context:   b.backgrounding.context,
	}

	if b.backgrounding.callback != nil {
		b.backgrounding.callback(b.client, event)
	} else if glog.V(3) {
		glog.V(3).Infof("ignore EXISTS event: %s", event)
	}
}

func (b *checkExistsBuilder) pathInForeground(path string) (*zk.Stat, error) {
	zkClient := b.client.ZookeeperClient()

	result, err := zkClient.newRetryLoop().callWithRetry(func() (interface{}, error) {
		if conn, err := zkClient.Conn(); err != nil {
			return nil, err
		} else {
			var exists bool
			var stat *zk.Stat
			var events <-chan zk.Event
			var err error

			if b.watching.watched || b.watching.watcher != nil {
				exists, stat, events, err = conn.ExistsW(path)

				if b.watching.watcher != nil {
					NewWatchers(b.watching.watcher).Watch(events)
				}
			} else {
				exists, stat, err = conn.Exists(path)
			}

			if err != nil {
				return nil, err
			} else if !exists {
				return stat, zk.ErrNoNode
			} else {
				return stat, nil
			}
		}
	})

	stat, _ := result.(*zk.Stat)

	return stat, err
}

func (b *checkExistsBuilder) Watched() CheckExistsBuilder {
	b.watching.watched = true

	return b
}

func (b *checkExistsBuilder) UsingWatcher(watcher Watcher) CheckExistsBuilder {
	b.watching.watcher = b.client.getNamespaceWatcher(watcher)

	return b
}

func (b *checkExistsBuilder) InBackground() CheckExistsBuilder {
	b.backgrounding = backgrounding{inBackground: true}

	return b
}

func (b *checkExistsBuilder) InBackgroundWithContext(context interface{}) CheckExistsBuilder {
	b.backgrounding = backgrounding{inBackground: true, context: context}

	return b
}

func (b *checkExistsBuilder) InBackgroundWithCallback(callback BackgroundCallback) CheckExistsBuilder {
	b.backgrounding = backgrounding{inBackground: true, callback: callback}

	return b
}

func (b *checkExistsBuilder) InBackgroundWithCallbackAndContext(callback BackgroundCallback, context interface{}) CheckExistsBuilder {
	b.backgrounding = backgrounding{inBackground: true, context: context, callback: callback}

	return b
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package lease

import (
	"fmt"
	"strings"
	"time"

	"github.com/juju/errors"
)

// These constants define the field names and type values used by documents in
// a lease collection. They *must* remain in sync with the bson marshalling
// annotations in leaseDoc and clockDoc.
const (
	// fieldType and fieldNamespace identify the Type and Namespace fields in
	// both leaseDoc and clockDoc.
	fieldType      = "type"
	fieldNamespace = "namespace"

	// typeLease and typeClock are the acceptable values for fieldType.
	typeLease = "lease"
	typeClock = "clock"

	// fieldLease* identify the fields in a leaseDoc.
	fieldLeaseName   = "name"
	fieldLeaseHolder = "holder"
	fieldLeaseExpiry = "expiry"
	fieldLeaseWriter = "writer"

	// fieldClock* identify the fields in a clockDoc.
	fieldClockWriters = "writers"
)

// toInt64 converts a local time.Time into a database value that doesn't
// silently lose precision.
func toInt64(t time.Time) int64 {
	return t.UnixNano()
}

// toTime converts a toInt64 result, as loaded from the db, back to a time.Time.
func toTime(v int64) time.Time {
	return time.Unix(0, v)
}

// For simplicity's sake, we impose the same restrictions on all strings used
// with the lease package: they may not be empty, and none of the following
// characters are allowed.
//   * '.' and '$' mean things to mongodb; we don't want to risk seeing them
//     in key names.
//   * '#' means something to the lease package and we don't want to risk
//     confusing ourselves.
//   * whitespace just seems like a bad idea.
const badCharacters = ".$# \t\r\n"

// validateString returns an error if the string is not valid.
func validateString(s string) error {
	if s == "" {
		return errors.New("string is empty")
	}
	if strings.ContainsAny(s, badCharacters) {
		return errors.New("string contains forbidden characters")
	}
	return nil
}

// leaseDocId returns the _id for the document holding details of the supplied
// namespace and lease.
func leaseDocId(namespace, lease string) string {
	return fmt.Sprintf("%s#%s#%s#", typeLease, namespace, lease)
}

// leaseDoc is used to serialise lease entries.
type leaseDoc struct {
	// Id is always "<Type>#<Namespace>#<Name>#", and <Type> is always "lease",
	// so that we can extract useful information from a stream of watcher events
	// without incurring extra DB hits.
	// Apart from checking validity on load, though, there's little reason
	// to use Id elsewhere; Namespace and Name are the sources of truth.
	Id        string `bson:"_id"`
	Type      string `bson:"type"`
	Namespace string `bson:"namespace"`
	Name      string `bson:"name"`

	// EnvUUID exists because state.multiEnvRunner can't handle structs
	// without `bson:"env-uuid"` fields. It's not necessary for the logic
	// in this package, though.
	EnvUUID string `bson:"env-uuid"`

	// Holder, Expiry, and Writer map directly to entry.
	Holder string `bson:"holder"`
	Expiry int64  `bson:"expiry"`
	Writer string `bson:"writer"`
}

// validate returns an error if any fields are invalid or inconsistent.
func (doc leaseDoc) validate() error {
	if doc.Type != typeLease {
		return errors.Errorf("invalid type %q", doc.Type)
	}
	// state.multiEnvRunner prepends environ ids in our documents, and
	// state.envStateCollection does not strip them out.
	if !strings.HasSuffix(doc.Id, leaseDocId(doc.Namespace, doc.Name)) {
		return errors.Errorf("inconsistent _id")
	}
	if err := validateString(doc.Holder); err != nil {
		return errors.Annotatef(err, "invalid holder")
	}
	if doc.Expiry == 0 {
		return errors.Errorf("invalid expiry")
	}
	if err := validateString(doc.Writer); err != nil {
		return errors.Annotatef(err, "invalid writer")
	}
	return nil
}

// entry returns the lease name and an entry corresponding to the document. If
// the document cannot be validated, it returns an error.
func (doc leaseDoc) entry() (string, entry, error) {
	if err := doc.validate(); err != nil {
		return "", entry{}, errors.Trace(err)
	}
	entry := entry{
		holder: doc.Holder,
		expiry: toTime(doc.Expiry),
		writer: doc.Writer,
	}
	return doc.Name, entry, nil
}

// newLeaseDoc returns a valid lease document encoding the supplied lease and
// entry in the supplied namespace, or an error.
func newLeaseDoc(namespace, name string, entry entry) (*leaseDoc, error) {
	doc := &leaseDoc{
		Id:        leaseDocId(namespace, name),
		Type:      typeLease,
		Namespace: namespace,
		Name:      name,
		Holder:    entry.holder,
		Expiry:    toInt64(entry.expiry),
		Writer:    entry.writer,
	}
	if err := doc.validate(); err != nil {
		return nil, errors.Trace(err)
	}
	return doc, nil
}

// clockDocId returns the _id for the document holding clock skew information
// for clients that have written in the supplied namespace.
func clockDocId(namespace string) string {
	return fmt.Sprintf("%s#%s#", typeClock, namespace)
}

// clockDoc is used to synchronise clients.
type clockDoc struct {
	// Id is always "<Type>#<Namespace>#", and <Type> is always "clock", for
	// consistency with leaseDoc and ease of querying within the collection.
	Id        string `bson:"_id"`
	Type      string `bson:"type"`
	Namespace string `bson:"namespace"`

	// EnvUUID exists because state.multiEnvRunner can't handle structs
	// without `bson:"env-uuid"` fields. It's not necessary for the logic
	// in this package, though.
	EnvUUID string `bson:"env-uuid"`

	// Writers holds a the latest acknowledged time for every known client.
	Writers map[string]int64 `bson:"writers"`
}

// validate returns an error if any fields are invalid or inconsistent.
func (doc clockDoc) validate() error {
	if doc.Type != typeClock {
		return errors.Errorf("invalid type %q", doc.Type)
	}
	// state.multiEnvRunner prepends environ ids in our documents, and
	// state.envStateCollection does not strip them out.
	if !strings.HasSuffix(doc.Id, clockDocId(doc.Namespace)) {
		return errors.Errorf("inconsistent _id")
	}
	for writer, written := range doc.Writers {
		if written == 0 {
			return errors.Errorf("invalid time for writer %q", writer)
		}
	}
	return nil
}

// skews returns clock skew information for all writers recorded in the
// document, given that the document was read between the supplied local
// times. It will return an error if the clock document is not valid, or
// if the times don't make sense.
func (doc clockDoc) skews(readAfter, readBefore time.Time) (map[string]Skew, error) {
	if err := doc.validate(); err != nil {
		return nil, errors.Trace(err)
	}
	if readBefore.Before(readAfter) {
		return nil, errors.New("end of read window preceded beginning")
	}
	skews := make(map[string]Skew)
	for writer, written := range doc.Writers {
		skews[writer] = Skew{
			LastWrite:  toTime(written),
			ReadAfter:  readAfter,
			ReadBefore: readBefore,
		}
	}
	return skews, nil
}

// newClockDoc returns an empty clockDoc for the supplied namespace.
func newClockDoc(namespace string) (*clockDoc, error) {
	doc := &clockDoc{
		Id:        clockDocId(namespace),
		Type:      typeClock,
		Namespace: namespace,
		Writers:   make(map[string]int64),
	}
	if err := doc.validate(); err != nil {
		return nil, errors.Trace(err)
	}
	return doc, nil
}
<commit_msg>show how fast time is going backwards<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package lease

import (
	"fmt"
	"strings"
	"time"

	"github.com/juju/errors"
)

// These constants define the field names and type values used by documents in
// a lease collection. They *must* remain in sync with the bson marshalling
// annotations in leaseDoc and clockDoc.
const (
	// fieldType and fieldNamespace identify the Type and Namespace fields in
	// both leaseDoc and clockDoc.
	fieldType      = "type"
	fieldNamespace = "namespace"

	// typeLease and typeClock are the acceptable values for fieldType.
	typeLease = "lease"
	typeClock = "clock"

	// fieldLease* identify the fields in a leaseDoc.
	fieldLeaseName   = "name"
	fieldLeaseHolder = "holder"
	fieldLeaseExpiry = "expiry"
	fieldLeaseWriter = "writer"

	// fieldClock* identify the fields in a clockDoc.
	fieldClockWriters = "writers"
)

// toInt64 converts a local time.Time into a database value that doesn't
// silently lose precision.
func toInt64(t time.Time) int64 {
	return t.UnixNano()
}

// toTime converts a toInt64 result, as loaded from the db, back to a time.Time.
func toTime(v int64) time.Time {
	return time.Unix(0, v)
}

// For simplicity's sake, we impose the same restrictions on all strings used
// with the lease package: they may not be empty, and none of the following
// characters are allowed.
//   * '.' and '$' mean things to mongodb; we don't want to risk seeing them
//     in key names.
//   * '#' means something to the lease package and we don't want to risk
//     confusing ourselves.
//   * whitespace just seems like a bad idea.
const badCharacters = ".$# \t\r\n"

// validateString returns an error if the string is not valid.
func validateString(s string) error {
	if s == "" {
		return errors.New("string is empty")
	}
	if strings.ContainsAny(s, badCharacters) {
		return errors.New("string contains forbidden characters")
	}
	return nil
}

// leaseDocId returns the _id for the document holding details of the supplied
// namespace and lease.
func leaseDocId(namespace, lease string) string {
	return fmt.Sprintf("%s#%s#%s#", typeLease, namespace, lease)
}

// leaseDoc is used to serialise lease entries.
type leaseDoc struct {
	// Id is always "<Type>#<Namespace>#<Name>#", and <Type> is always "lease",
	// so that we can extract useful information from a stream of watcher events
	// without incurring extra DB hits.
	// Apart from checking validity on load, though, there's little reason
	// to use Id elsewhere; Namespace and Name are the sources of truth.
	Id        string `bson:"_id"`
	Type      string `bson:"type"`
	Namespace string `bson:"namespace"`
	Name      string `bson:"name"`

	// EnvUUID exists because state.multiEnvRunner can't handle structs
	// without `bson:"env-uuid"` fields. It's not necessary for the logic
	// in this package, though.
	EnvUUID string `bson:"env-uuid"`

	// Holder, Expiry, and Writer map directly to entry.
	Holder string `bson:"holder"`
	Expiry int64  `bson:"expiry"`
	Writer string `bson:"writer"`
}

// validate returns an error if any fields are invalid or inconsistent.
func (doc leaseDoc) validate() error {
	if doc.Type != typeLease {
		return errors.Errorf("invalid type %q", doc.Type)
	}
	// state.multiEnvRunner prepends environ ids in our documents, and
	// state.envStateCollection does not strip them out.
	if !strings.HasSuffix(doc.Id, leaseDocId(doc.Namespace, doc.Name)) {
		return errors.Errorf("inconsistent _id")
	}
	if err := validateString(doc.Holder); err != nil {
		return errors.Annotatef(err, "invalid holder")
	}
	if doc.Expiry == 0 {
		return errors.Errorf("invalid expiry")
	}
	if err := validateString(doc.Writer); err != nil {
		return errors.Annotatef(err, "invalid writer")
	}
	return nil
}

// entry returns the lease name and an entry corresponding to the document. If
// the document cannot be validated, it returns an error.
func (doc leaseDoc) entry() (string, entry, error) {
	if err := doc.validate(); err != nil {
		return "", entry{}, errors.Trace(err)
	}
	entry := entry{
		holder: doc.Holder,
		expiry: toTime(doc.Expiry),
		writer: doc.Writer,
	}
	return doc.Name, entry, nil
}

// newLeaseDoc returns a valid lease document encoding the supplied lease and
// entry in the supplied namespace, or an error.
func newLeaseDoc(namespace, name string, entry entry) (*leaseDoc, error) {
	doc := &leaseDoc{
		Id:        leaseDocId(namespace, name),
		Type:      typeLease,
		Namespace: namespace,
		Name:      name,
		Holder:    entry.holder,
		Expiry:    toInt64(entry.expiry),
		Writer:    entry.writer,
	}
	if err := doc.validate(); err != nil {
		return nil, errors.Trace(err)
	}
	return doc, nil
}

// clockDocId returns the _id for the document holding clock skew information
// for clients that have written in the supplied namespace.
func clockDocId(namespace string) string {
	return fmt.Sprintf("%s#%s#", typeClock, namespace)
}

// clockDoc is used to synchronise clients.
type clockDoc struct {
	// Id is always "<Type>#<Namespace>#", and <Type> is always "clock", for
	// consistency with leaseDoc and ease of querying within the collection.
	Id        string `bson:"_id"`
	Type      string `bson:"type"`
	Namespace string `bson:"namespace"`

	// EnvUUID exists because state.multiEnvRunner can't handle structs
	// without `bson:"env-uuid"` fields. It's not necessary for the logic
	// in this package, though.
	EnvUUID string `bson:"env-uuid"`

	// Writers holds a the latest acknowledged time for every known client.
	Writers map[string]int64 `bson:"writers"`
}

// validate returns an error if any fields are invalid or inconsistent.
func (doc clockDoc) validate() error {
	if doc.Type != typeClock {
		return errors.Errorf("invalid type %q", doc.Type)
	}
	// state.multiEnvRunner prepends environ ids in our documents, and
	// state.envStateCollection does not strip them out.
	if !strings.HasSuffix(doc.Id, clockDocId(doc.Namespace)) {
		return errors.Errorf("inconsistent _id")
	}
	for writer, written := range doc.Writers {
		if written == 0 {
			return errors.Errorf("invalid time for writer %q", writer)
		}
	}
	return nil
}

// skews returns clock skew information for all writers recorded in the
// document, given that the document was read between the supplied local
// times. It will return an error if the clock document is not valid, or
// if the times don't make sense.
func (doc clockDoc) skews(readAfter, readBefore time.Time) (map[string]Skew, error) {
	if err := doc.validate(); err != nil {
		return nil, errors.Trace(err)
	}
	if readBefore.Before(readAfter) {
		difference := readAfter.Sub(readBefore)
		return nil, errors.Errorf("end of read window preceded beginning (%s)", difference)
	}
	skews := make(map[string]Skew)
	for writer, written := range doc.Writers {
		skews[writer] = Skew{
			LastWrite:  toTime(written),
			ReadAfter:  readAfter,
			ReadBefore: readBefore,
		}
	}
	return skews, nil
}

// newClockDoc returns an empty clockDoc for the supplied namespace.
func newClockDoc(namespace string) (*clockDoc, error) {
	doc := &clockDoc{
		Id:        clockDocId(namespace),
		Type:      typeClock,
		Namespace: namespace,
		Writers:   make(map[string]int64),
	}
	if err := doc.validate(); err != nil {
		return nil, errors.Trace(err)
	}
	return doc, nil
}
<|endoftext|>


<commit_before>package routernode

import (
	"context"

	"github.com/jacksontj/dataman/src/query"
	"github.com/jacksontj/dataman/src/router_node/client_manager"
	"github.com/jacksontj/dataman/src/router_node/metadata"
)

// TODO: remove this method? Doesn't do much
// TODO: use same client as everyone else (with some LRU/LFU cache of the connections?)
// Take a query and send it to a given destination
func Query(ctx context.Context, clientManager clientmanager.ClientManager, datasourceInstance *metadata.DatasourceInstance, q *query.Query) (*query.Result, error) {
	// Create our own copy of query

	// get the client
	client, err := clientManager.GetClient(datasourceInstance)
	if err != nil {
		return nil, err
	}
	// send the query
	return client.DoQuery(ctx, q)
}
<commit_msg>More detail in comment<commit_after>package routernode

import (
	"context"

	"github.com/jacksontj/dataman/src/query"
	"github.com/jacksontj/dataman/src/router_node/client_manager"
	"github.com/jacksontj/dataman/src/router_node/metadata"
)

// TODO: remove this method? Doesn't do much. Once we support sending things to more than just the primary
// this won't be helpful (since each call will need to know what is acceptable)
// TODO: use same client as everyone else (with some LRU/LFU cache of the connections?)
// Take a query and send it to a given destination
func Query(ctx context.Context, clientManager clientmanager.ClientManager, datasourceInstance *metadata.DatasourceInstance, q *query.Query) (*query.Result, error) {
	// Create our own copy of query

	// get the client
	client, err := clientManager.GetClient(datasourceInstance)
	if err != nil {
		return nil, err
	}
	// send the query
	return client.DoQuery(ctx, q)
}
<|endoftext|>


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

package fauxfile

import (
	"errors"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"
	"bytes"
	"time"
)

func GetPathError(path string, message string) error {
	return &os.PathError{
		Path: path,
		Err:  errors.New(message),
	}
}

type MockFilesystem struct {
	cwd  *MockFileInfo
	root *MockFileInfo
}

func NewMockFilesystem() *MockFilesystem {
	root := &MockFileInfo{
		name:       "/",
		filesystem: nil,
		mode:       os.ModeDir | 0755,
		modified:   time.Now(),
		data:       nil,
		parent:     nil,
		children:   map[string]*MockFileInfo{},
	}
	mf := &MockFilesystem{
		cwd:  root,
		root: root,
	}
	root.filesystem = mf
	return mf
}

func (mf *MockFilesystem) getpath(path string) string {
	if filepath.IsAbs(path) {
		return filepath.Clean(path)
	}
	return filepath.Join(mf.cwd.path(), path)
}

func (mf *MockFilesystem) resolve(path string) (*MockFileInfo, error) {
	path = mf.getpath(path)
	parts := strings.Split(path, string(filepath.Separator))
	ptr := mf.root
	for _, part := range parts {
		if part == "" {
			continue
		}
		if child := ptr.Child(part); child != nil {
			ptr = ptr.Child(part)
		} else {
			return nil, GetPathError(path, "Path does not exist")
		}
	}
	return ptr, nil
}

func (mf *MockFilesystem) exists(path string) bool {
	_, err := mf.resolve(path)
	return err == nil
}

func (mf *MockFilesystem) Chdir(dir string) error {
	fi, err := mf.resolve(dir)
	if err == nil {
		if fi.IsDir() {
			mf.cwd = fi
			return nil
		} else {
			return GetPathError(dir, "Path is not a directory")
		}
	}
	return err
}

func (mf *MockFilesystem) Mkdir(name string, perm os.FileMode) error {
	path := mf.getpath(name)
	parentpath, dirname := filepath.Split(path)
	fi, err := mf.resolve(parentpath)
	if err != nil {
		return err
	}
	if child := fi.Child(dirname); child != nil {
		return GetPathError(path, "Path already exists")
	}
	fi.children[dirname] = &MockFileInfo{
		name:       dirname,
		filesystem: mf,
		mode:       perm | os.ModeDir,
		modified:   time.Now(),
		data:       new(bytes.Buffer),
		parent:     fi,
		children:   map[string]*MockFileInfo{},
	}
	fi.modified = time.Now() // Update directory's timestamp.
	return nil
}

func (mf *MockFilesystem) MkdirAll(path string, perm os.FileMode) error {
	path = mf.getpath(path)
	parts := strings.Split(path, string(filepath.Separator))
	base := "/"
	for _, part := range parts {
		if part == "" {
			continue
		}
		base = filepath.Join(base, part)
		if err := mf.Mkdir(base, perm); err != nil {
			return err
		}
	}
	return nil
}

func (mf *MockFilesystem) Remove(name string) error {
	fi, err := mf.resolve(name)
	if err != nil {
		return err
	}
	if len(fi.Children()) > 0 {
		return GetPathError(name, "Directory contains children")
	}
	delete(fi.Parent().Children(), fi.name)
	fi.Parent().modified = time.Now()
	return nil
}

func (mf *MockFilesystem) RemoveAll(path string) error {
	fi, err := mf.resolve(path)
	if err != nil {
		return err
	}
	delete(fi.Parent().Children(), fi.name)
	fi.Parent().modified = time.Now()
	return nil
}

func (mf *MockFilesystem) Rename(oldname string, newname string) error {
	return errors.New("Not implemented")
}

func (mf *MockFilesystem) Create(name string) (file File, err error) {
	path := mf.getpath(name)
	dir, filename := filepath.Split(path)
	fi, err := mf.resolve(dir)
	if err != nil {
		return nil, err
	}
	fi.children[filename] = &MockFileInfo{
		name:       filename,
		filesystem: mf,
		mode:       0666,
		modified:   time.Now(),
		data:       new(bytes.Buffer),
		parent:     fi,
		children:   nil,
	}
	fi.modified = time.Now()
	f := &MockFile{
		filesystem: mf,
		closed: false,
		path: path,
	}
	return f, nil
}

func (mf *MockFilesystem) Open(name string) (file File, err error) {
	_, err = mf.resolve(name)
	if err != nil {
		return nil, err
	}
	f := &MockFile{
		filesystem: mf,
		closed: false,
		path: name,
	}
	return f, nil
}

func (mf *MockFilesystem) OpenFile(name string, flag int, perm os.FileMode) (file File, err error) {
	return nil, errors.New("Not implemented")
}

type MockFile struct {
	path       string
	closed     bool
	filesystem *MockFilesystem
}

func (mf *MockFile) stat() (mfi *MockFileInfo, err error) {
	return mf.filesystem.resolve(mf.path)
}

func (mf *MockFile) Chdir() error {
	return mf.filesystem.Chdir(filepath.Dir(mf.path))
}

func (mf *MockFile) Chmod(mode os.FileMode) error {
	var (
		mfi *MockFileInfo
		err error
	)
	if mfi, err = mf.stat(); err != nil {
		return err
	}
	mfi.mode = mode
	return nil
}

func (mf *MockFile) Close() error {
	mf.closed = true
	return nil
}

func (mf *MockFile) Name() string {
	_, filename := filepath.Split(mf.path)
	return filename
}

func (mf *MockFile) Read(b []byte) (n int, err error) {
	var mfi *MockFileInfo
	if mfi, err = mf.stat(); err != nil {
		return 0, err
	}
	return mfi.data.Read(b)
}

func (mf *MockFile) ReadAt(b []byte, off int64) (n int, err error) {
	return 0, errors.New("Not implemented")
}

func (mf *MockFile) Readdir(n int) (fi []os.FileInfo, err error) {
	// TODO: Enable returning additional elements in subsequent calls.
	var (
		mfi *MockFileInfo
	)
	fi = make([]os.FileInfo, 0)
	if mfi, err = mf.stat(); err != nil {
		return nil, err
	}
	limit := len(mfi.children)
	if n > 0 {
		limit = n
	}
	if len(mfi.children) < limit {
		err = io.EOF
	}
	if len(mfi.children) == 0 {
		return
	}
	i := 0
	for _, child := range mfi.children {
		if i == limit {
			break
		}
		fi = append(fi, child)
		i++
	}
	return
}

func (mf *MockFile) Readdirnames(n int) (names []string, err error) {
	fi, err := mf.Readdir(n)
	names = make([]string, len(fi))
	for i, f := range fi {
		names[i] = f.Name()
	}
	return names, err
}

func (mf *MockFile) Seek(offset int64, whence int) (ret int64, err error) {
	return 0, errors.New("Not implemented")
}

func (mf *MockFile) Stat() (fi os.FileInfo, err error) {
	return mf.stat()
}

func (mf *MockFile) Sync() (err error) {
	return nil
}

func (mf *MockFile) Truncate(size int64) error {
	var mfi *MockFileInfo
	if mfi, err = mf.stat(); err != nil {
		return 0, err
	}
	return mfi.data.Truncate(size)
}

func (mf *MockFile) Write(b []byte) (n int, err error) {
	var mfi *MockFileInfo
	if mfi, err = mf.stat(); err != nil {
		return 0, err
	}
	return mfi.data.Write(b)
}

func (mf *MockFile) WriteAt(b []byte, off int64) (n int, err error) {
	return 0, errors.New("Not implemented")
}

func (mf *MockFile) WriteString(s string) (ret int, err error) {
	var mfi *MockFileInfo
	if mfi, err = mf.stat(); err != nil {
		return 0, err
	}
	return mfi.data.Write(s)
}

type MockFileInfo struct {
	data       *bytes.Buffer
	name       string
	filesystem *MockFilesystem
	mode       os.FileMode
	modified   time.Time
	parent     *MockFileInfo
	children   map[string]*MockFileInfo
}

func (mfi *MockFileInfo) path() string {
	ptr := mfi
	filepath := mfi.name
	for ptr != mfi.filesystem.root {
		ptr = ptr.parent
		filepath = path.Join(ptr.name, filepath)
	}
	return filepath
}

func (mfi *MockFileInfo) Parent() *MockFileInfo {
	return mfi.parent
}

func (mfi *MockFileInfo) Children() map[string]*MockFileInfo {
	return mfi.children
}

func (mfi *MockFileInfo) Child(name string) *MockFileInfo {
	return mfi.children[name]
}

func (mfi *MockFileInfo) Name() string {
	return mfi.name
}

func (mfi *MockFileInfo) Size() int64 {
	return int64(mfi.data.Len())
}

func (mfi *MockFileInfo) Mode() os.FileMode {
	return mfi.mode
}

func (mfi *MockFileInfo) ModTime() time.Time {
	return mfi.modified
}

func (mfi *MockFileInfo) IsDir() bool {
	return mfi.mode.IsDir()
}

func (mfi *MockFileInfo) Sys() interface{} {
	return nil
}
<commit_msg>Fixing breaking tests<commit_after>// Copyright 2012 Arne Roomann-Kurrik
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fauxfile

import (
	"bytes"
	"errors"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"
)

func GetPathError(path string, message string) error {
	return &os.PathError{
		Path: path,
		Err:  errors.New(message),
	}
}

type MockFilesystem struct {
	cwd  *MockFileInfo
	root *MockFileInfo
}

func NewMockFilesystem() *MockFilesystem {
	root := &MockFileInfo{
		name:       "/",
		filesystem: nil,
		mode:       os.ModeDir | 0755,
		modified:   time.Now(),
		data:       nil,
		parent:     nil,
		children:   map[string]*MockFileInfo{},
	}
	mf := &MockFilesystem{
		cwd:  root,
		root: root,
	}
	root.filesystem = mf
	return mf
}

func (mf *MockFilesystem) getpath(path string) string {
	if filepath.IsAbs(path) {
		return filepath.Clean(path)
	}
	return filepath.Join(mf.cwd.path(), path)
}

func (mf *MockFilesystem) resolve(path string) (*MockFileInfo, error) {
	path = mf.getpath(path)
	parts := strings.Split(path, string(filepath.Separator))
	ptr := mf.root
	for _, part := range parts {
		if part == "" {
			continue
		}
		if child := ptr.Child(part); child != nil {
			ptr = ptr.Child(part)
		} else {
			return nil, GetPathError(path, "Path does not exist")
		}
	}
	return ptr, nil
}

func (mf *MockFilesystem) exists(path string) bool {
	_, err := mf.resolve(path)
	return err == nil
}

func (mf *MockFilesystem) Chdir(dir string) error {
	fi, err := mf.resolve(dir)
	if err == nil {
		if fi.IsDir() {
			mf.cwd = fi
			return nil
		} else {
			return GetPathError(dir, "Path is not a directory")
		}
	}
	return err
}

func (mf *MockFilesystem) Mkdir(name string, perm os.FileMode) error {
	path := mf.getpath(name)
	parentpath, dirname := filepath.Split(path)
	fi, err := mf.resolve(parentpath)
	if err != nil {
		return err
	}
	if child := fi.Child(dirname); child != nil {
		return GetPathError(path, "Path already exists")
	}
	fi.children[dirname] = &MockFileInfo{
		name:       dirname,
		filesystem: mf,
		mode:       perm | os.ModeDir,
		modified:   time.Now(),
		data:       new(bytes.Buffer),
		parent:     fi,
		children:   map[string]*MockFileInfo{},
	}
	fi.modified = time.Now() // Update directory's timestamp.
	return nil
}

func (mf *MockFilesystem) MkdirAll(path string, perm os.FileMode) error {
	path = mf.getpath(path)
	parts := strings.Split(path, string(filepath.Separator))
	base := "/"
	for _, part := range parts {
		if part == "" {
			continue
		}
		base = filepath.Join(base, part)
		if err := mf.Mkdir(base, perm); err != nil {
			return err
		}
	}
	return nil
}

func (mf *MockFilesystem) Remove(name string) error {
	fi, err := mf.resolve(name)
	if err != nil {
		return err
	}
	if len(fi.Children()) > 0 {
		return GetPathError(name, "Directory contains children")
	}
	delete(fi.Parent().Children(), fi.name)
	fi.Parent().modified = time.Now()
	return nil
}

func (mf *MockFilesystem) RemoveAll(path string) error {
	fi, err := mf.resolve(path)
	if err != nil {
		return err
	}
	delete(fi.Parent().Children(), fi.name)
	fi.Parent().modified = time.Now()
	return nil
}

func (mf *MockFilesystem) Rename(oldname string, newname string) error {
	return errors.New("Not implemented")
}

func (mf *MockFilesystem) Create(name string) (file File, err error) {
	path := mf.getpath(name)
	dir, filename := filepath.Split(path)
	fi, err := mf.resolve(dir)
	if err != nil {
		return nil, err
	}
	fi.children[filename] = &MockFileInfo{
		name:       filename,
		filesystem: mf,
		mode:       0666,
		modified:   time.Now(),
		data:       new(bytes.Buffer),
		parent:     fi,
		children:   nil,
	}
	fi.modified = time.Now()
	f := &MockFile{
		filesystem: mf,
		closed:     false,
		path:       path,
	}
	return f, nil
}

func (mf *MockFilesystem) Open(name string) (file File, err error) {
	_, err = mf.resolve(name)
	if err != nil {
		return nil, err
	}
	f := &MockFile{
		filesystem: mf,
		closed:     false,
		path:       name,
	}
	return f, nil
}

func (mf *MockFilesystem) OpenFile(name string, flag int, perm os.FileMode) (file File, err error) {
	return nil, errors.New("Not implemented")
}

type MockFile struct {
	path       string
	closed     bool
	filesystem *MockFilesystem
}

func (mf *MockFile) stat() (mfi *MockFileInfo, err error) {
	return mf.filesystem.resolve(mf.path)
}

func (mf *MockFile) Chdir() error {
	return mf.filesystem.Chdir(filepath.Dir(mf.path))
}

func (mf *MockFile) Chmod(mode os.FileMode) error {
	var (
		mfi *MockFileInfo
		err error
	)
	if mfi, err = mf.stat(); err != nil {
		return err
	}
	mfi.mode = mode
	return nil
}

func (mf *MockFile) Close() error {
	mf.closed = true
	return nil
}

func (mf *MockFile) Name() string {
	_, filename := filepath.Split(mf.path)
	return filename
}

func (mf *MockFile) Read(b []byte) (n int, err error) {
	var mfi *MockFileInfo
	if mfi, err = mf.stat(); err != nil {
		return 0, err
	}
	return mfi.data.Read(b)
}

func (mf *MockFile) ReadAt(b []byte, off int64) (n int, err error) {
	return 0, errors.New("Not implemented")
}

func (mf *MockFile) Readdir(n int) (fi []os.FileInfo, err error) {
	// TODO: Enable returning additional elements in subsequent calls.
	var (
		mfi *MockFileInfo
	)
	fi = make([]os.FileInfo, 0)
	if mfi, err = mf.stat(); err != nil {
		return nil, err
	}
	limit := len(mfi.children)
	if n > 0 {
		limit = n
	}
	if len(mfi.children) < limit {
		err = io.EOF
	}
	if len(mfi.children) == 0 {
		return
	}
	i := 0
	for _, child := range mfi.children {
		if i == limit {
			break
		}
		fi = append(fi, child)
		i++
	}
	return
}

func (mf *MockFile) Readdirnames(n int) (names []string, err error) {
	fi, err := mf.Readdir(n)
	names = make([]string, len(fi))
	for i, f := range fi {
		names[i] = f.Name()
	}
	return names, err
}

func (mf *MockFile) Seek(offset int64, whence int) (ret int64, err error) {
	return 0, errors.New("Not implemented")
}

func (mf *MockFile) Stat() (fi os.FileInfo, err error) {
	return mf.stat()
}

func (mf *MockFile) Sync() (err error) {
	return nil
}

func (mf *MockFile) Truncate(size int64) error {
	var (
		mfi *MockFileInfo
		err error
	)
	if mfi, err = mf.stat(); err != nil {
		return err
	}
	mfi.data.Truncate(int(size))
	return nil
}

func (mf *MockFile) Write(b []byte) (n int, err error) {
	var mfi *MockFileInfo
	if mfi, err = mf.stat(); err != nil {
		return 0, err
	}
	return mfi.data.Write(b)
}

func (mf *MockFile) WriteAt(b []byte, off int64) (n int, err error) {
	return 0, errors.New("Not implemented")
}

func (mf *MockFile) WriteString(s string) (ret int, err error) {
	var mfi *MockFileInfo
	if mfi, err = mf.stat(); err != nil {
		return 0, err
	}
	return mfi.data.WriteString(s)
}

type MockFileInfo struct {
	data       *bytes.Buffer
	name       string
	filesystem *MockFilesystem
	mode       os.FileMode
	modified   time.Time
	parent     *MockFileInfo
	children   map[string]*MockFileInfo
}

func (mfi *MockFileInfo) path() string {
	ptr := mfi
	filepath := mfi.name
	for ptr != mfi.filesystem.root {
		ptr = ptr.parent
		filepath = path.Join(ptr.name, filepath)
	}
	return filepath
}

func (mfi *MockFileInfo) Parent() *MockFileInfo {
	return mfi.parent
}

func (mfi *MockFileInfo) Children() map[string]*MockFileInfo {
	return mfi.children
}

func (mfi *MockFileInfo) Child(name string) *MockFileInfo {
	return mfi.children[name]
}

func (mfi *MockFileInfo) Name() string {
	return mfi.name
}

func (mfi *MockFileInfo) Size() int64 {
	return int64(mfi.data.Len())
}

func (mfi *MockFileInfo) Mode() os.FileMode {
	return mfi.mode
}

func (mfi *MockFileInfo) ModTime() time.Time {
	return mfi.modified
}

func (mfi *MockFileInfo) IsDir() bool {
	return mfi.mode.IsDir()
}

func (mfi *MockFileInfo) Sys() interface{} {
	return nil
}
<|endoftext|>


<commit_before>package middleware

import (
	"bufio"
	"errors"
	"net"
	"net/http"
	"time"
)

// ResponseRecorder is a type of http.ResponseWriter that captures
// the status code written to it and also the size of the body
// written in the response. A status code does not have
// to be written, however, in which case 200 must be assumed.
// It is best to have the constructor initialize this type
// with that default status code.
//
// Setting the Replacer field allows middlewares to type-assert
// the http.ResponseWriter to ResponseRecorder and set their own
// placeholder values for logging utilities to use.
//
// Beware when accessing the Replacer value; it may be nil!
type ResponseRecorder struct {
	http.ResponseWriter
	Replacer Replacer
	status   int
	size     int
	start    time.Time
}

// NewResponseRecorder makes and returns a new responseRecorder,
// which captures the HTTP Status code from the ResponseWriter
// and also the length of the response body written through it.
// Because a status is not set unless WriteHeader is called
// explicitly, this constructor initializes with a status code
// of 200 to cover the default case.
func NewResponseRecorder(w http.ResponseWriter) *ResponseRecorder {
	return &ResponseRecorder{
		ResponseWriter: w,
		status:         http.StatusOK,
		start:          time.Now(),
	}
}

// WriteHeader records the status code and calls the
// underlying ResponseWriter's WriteHeader method.
func (r *ResponseRecorder) WriteHeader(status int) {
	r.status = status
	r.ResponseWriter.WriteHeader(status)
}

// Write is a wrapper that records the size of the body
// that gets written.
func (r *ResponseRecorder) Write(buf []byte) (int, error) {
	n, err := r.ResponseWriter.Write(buf)
	if err == nil {
		r.size += n
	}
	return n, err
}

// Size is a Getter to size property
func (r *ResponseRecorder) Size() int {
	return r.size
}

// Status is a Getter to status property
func (r *ResponseRecorder) Status() int {
	return r.status
}

// Hijack implements http.Hijacker. It simply wraps the underlying
// ResponseWriter's Hijack method if there is one, or returns an error.
func (r *ResponseRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	if hj, ok := r.ResponseWriter.(http.Hijacker); ok {
		return hj.Hijack()
	}
	return nil, nil, errors.New("not a Hijacker")
}

// Flush implements http.Flusher. It simply wraps the underlying
// ResponseWriter's Flush method if there is one, or does nothing.
func (r *ResponseRecorder) Flush() {
	if f, ok := r.ResponseWriter.(http.Flusher); ok {
		f.Flush()
	}
}
<commit_msg>panic if not a Flusher<commit_after>package middleware

import (
	"bufio"
	"errors"
	"net"
	"net/http"
	"time"
)

// ResponseRecorder is a type of http.ResponseWriter that captures
// the status code written to it and also the size of the body
// written in the response. A status code does not have
// to be written, however, in which case 200 must be assumed.
// It is best to have the constructor initialize this type
// with that default status code.
//
// Setting the Replacer field allows middlewares to type-assert
// the http.ResponseWriter to ResponseRecorder and set their own
// placeholder values for logging utilities to use.
//
// Beware when accessing the Replacer value; it may be nil!
type ResponseRecorder struct {
	http.ResponseWriter
	Replacer Replacer
	status   int
	size     int
	start    time.Time
}

// NewResponseRecorder makes and returns a new responseRecorder,
// which captures the HTTP Status code from the ResponseWriter
// and also the length of the response body written through it.
// Because a status is not set unless WriteHeader is called
// explicitly, this constructor initializes with a status code
// of 200 to cover the default case.
func NewResponseRecorder(w http.ResponseWriter) *ResponseRecorder {
	return &ResponseRecorder{
		ResponseWriter: w,
		status:         http.StatusOK,
		start:          time.Now(),
	}
}

// WriteHeader records the status code and calls the
// underlying ResponseWriter's WriteHeader method.
func (r *ResponseRecorder) WriteHeader(status int) {
	r.status = status
	r.ResponseWriter.WriteHeader(status)
}

// Write is a wrapper that records the size of the body
// that gets written.
func (r *ResponseRecorder) Write(buf []byte) (int, error) {
	n, err := r.ResponseWriter.Write(buf)
	if err == nil {
		r.size += n
	}
	return n, err
}

// Size is a Getter to size property
func (r *ResponseRecorder) Size() int {
	return r.size
}

// Status is a Getter to status property
func (r *ResponseRecorder) Status() int {
	return r.status
}

// Hijack implements http.Hijacker. It simply wraps the underlying
// ResponseWriter's Hijack method if there is one, or returns an error.
func (r *ResponseRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	if hj, ok := r.ResponseWriter.(http.Hijacker); ok {
		return hj.Hijack()
	}
	return nil, nil, errors.New("not a Hijacker")
}

// Flush implements http.Flusher. It simply wraps the underlying
// ResponseWriter's Flush method if there is one, or does nothing.
func (r *ResponseRecorder) Flush() {
	if f, ok := r.ResponseWriter.(http.Flusher); ok {
		f.Flush()
	} else {
		panic("not a Flusher") // should be recovered at the beginning of middleware stack
	}
}
<|endoftext|>


<commit_before>package middlewares

import (
	"net/http"

	"github.com/containous/traefik/log"
	"github.com/urfave/negroni"
)

// RecoverHandler recovers from a panic in http handlers
func RecoverHandler(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		defer recoverFunc(w)
		next.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

// NegroniRecoverHandler recovers from a panic in negroni handlers
func NegroniRecoverHandler() negroni.Handler {
	fn := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		defer recoverFunc(w)
		next.ServeHTTP(w, r)
	}
	return negroni.HandlerFunc(fn)
}

func recoverFunc(w http.ResponseWriter) {
	if err := recover(); err != nil {
		log.Errorf("Recovered from panic in http handler: %+v", err)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
}
<commit_msg>Fix recover from panic handler<commit_after>package middlewares

import (
	"net/http"

	"github.com/containous/traefik/log"
	"github.com/urfave/negroni"
)

// RecoverHandler recovers from a panic in http handlers
func RecoverHandler(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		defer recoverFunc(w)
		next.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

// NegroniRecoverHandler recovers from a panic in negroni handlers
func NegroniRecoverHandler() negroni.Handler {
	fn := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		defer recoverFunc(w)
		next.ServeHTTP(w, r)
	}
	return negroni.HandlerFunc(fn)
}

func recoverFunc(w http.ResponseWriter) {
	if err := recover(); err != nil {
		if shouldLogPanic(err) {
			log.Errorf("Recovered from panic in http handler: %+v", err)
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		} else {
			log.Debugf("HTTP handler has been aborted: %v", err)
		}
	}
}

// https://github.com/golang/go/blob/c33153f7b416c03983324b3e8f869ce1116d84bc/src/net/http/httputil/reverseproxy.go#L284
func shouldLogPanic(panicValue interface{}) bool {
	return panicValue != nil && panicValue != http.ErrAbortHandler
}
<|endoftext|>


<commit_before>// +build wasm

package transport

import (
	"syscall/js"

	"github.com/emicklei/melrose/core"
	"github.com/emicklei/melrose/notify"
)

func init() { Initializer = wasmInitialize }

func wasmInitialize() {
	if core.IsDebug() {
		notify.Debugf("transport.init: use WASMmidiTransporter")
	}
	Factory = func() Transporter {
		return WASMmidiTransporter{}
	}
}

type WASMmidiTransporter struct{}

func (t WASMmidiTransporter) HasInputCapability() bool {
	return false
}
func (t WASMmidiTransporter) PrintInfo(inID, outID int) {

}
func (t WASMmidiTransporter) DefaultOutputDeviceID() int {
	return 0
}
func (t WASMmidiTransporter) DefaultInputDeviceID() int {
	return 0
}
func (t WASMmidiTransporter) NewMIDIOut(id int) (MIDIOut, error) {
	return WASMMidiOut{id: id}, nil

}
func (t WASMmidiTransporter) NewMIDIIn(id int) (MIDIIn, error) {
	return WASMMidiIn{}, nil
}
func (t WASMmidiTransporter) NewMIDIListener(MIDIIn) MIDIListener {
	return nil
}

type WASMMidiOut struct {
	id int
}

func (m WASMMidiOut) WriteShort(status int64, data1 int64, data2 int64) error {
	// MIDI_send(deviceID, status, pitch, velocity)
	js.Global().Call("MIDI_send", m.id, uint8(status), uint8(data1), uint8(data2))
	return nil
}
func (m WASMMidiOut) Close() error {
	return nil
}

type WASMMidiIn struct{}

func (m WASMMidiIn) Close() error {
	return nil
}
<commit_msg>rename js callback<commit_after>// +build wasm

package transport

import (
	"syscall/js"

	"github.com/emicklei/melrose/core"
	"github.com/emicklei/melrose/notify"
)

func init() { Initializer = wasmInitialize }

func wasmInitialize() {
	if core.IsDebug() {
		notify.Debugf("transport.init: use WASMmidiTransporter")
	}
	Factory = func() Transporter {
		return WASMmidiTransporter{}
	}
}

type WASMmidiTransporter struct{}

func (t WASMmidiTransporter) HasInputCapability() bool {
	return false
}
func (t WASMmidiTransporter) PrintInfo(inID, outID int) {

}
func (t WASMmidiTransporter) DefaultOutputDeviceID() int {
	return 0
}
func (t WASMmidiTransporter) DefaultInputDeviceID() int {
	return 0
}
func (t WASMmidiTransporter) NewMIDIOut(id int) (MIDIOut, error) {
	return WASMMidiOut{id: id}, nil

}
func (t WASMmidiTransporter) NewMIDIIn(id int) (MIDIIn, error) {
	return WASMMidiIn{}, nil
}
func (t WASMmidiTransporter) NewMIDIListener(MIDIIn) MIDIListener {
	return nil
}

type WASMMidiOut struct {
	id int
}

func (m WASMMidiOut) WriteShort(status int64, data1 int64, data2 int64) error {
	// MIDI_send(deviceID, status, pitch, velocity)
	js.Global().Call("melrose_send", m.id, uint8(status), uint8(data1), uint8(data2))
	return nil
}
func (m WASMMidiOut) Close() error {
	return nil
}

type WASMMidiIn struct{}

func (m WASMMidiIn) Close() error {
	return nil
}
<|endoftext|>