<commit_before>package analytics

//
// dependencies
//

import "github.com/jehiah/go-strftime"
import "github.com/nu7hatch/gouuid"
import . "encoding/json"
import "net/http"
import "bytes"
import "time"
import "log"

//
// Library version
//

const Version = "0.0.1"

//
// Default API end-point
//

const api = "https://api.segment.io"

//
// Segment.io client
//

type Client struct {
	Debug         bool
	BufferSize    int
	FlushInterval time.Duration
	Endpoint      string
	Key           string
	buffer        []*interface{}
}

//
// Message context library
//

type contextLibrary struct {
	Name    string `json:"name"`
	Version string `json:"version"`
}

//
// Message context
//

type context struct {
	Library contextLibrary `json:"library"`
}

//
// Identify message
//

type identify struct {
	Action    string      `json:"action"`
	Traits    interface{} `json:"trailts"`
	Timestamp string      `json:"timestamp"`
}

//
// Alias message
//

type alias struct {
	Action     string `json:"action"`
	PreviousId string `json:"previousId"`
	Timestamp  string `json:"timestamp"`
}

//
// Track message
//

type track struct {
	Action     string      `json:"action"`
	Event      string      `json:"event"`
	Properties interface{} `json:"properties"`
	Timestamp  string      `json:"timestamp"`
}

//
// Group message
//

type group struct {
	Action    string      `json:"action"`
	GroupId   string      `json:"groupId"`
	Traits    interface{} `json:"trailts"`
	Timestamp string      `json:"timestamp"`
}

//
// Page message
//

type page struct {
	Action     string      `json:"action"`
	Category   string      `json:"category"`
	Name       string      `json:"name"`
	Properties interface{} `json:"properties"`
	Timestamp  string      `json:"timestamp"`
}

//
// Batch message
//

type batch struct {
	Context   context        `json:"context"`
	RequestId string         `json:"requestId"`
	Messages  []*interface{} `json:"batch"`
}

//
// Return a new Segment.io client
// with the given write key.
//

func New(key string) (c *Client) {
	defer func() {
		go func() {
			for {
				time.Sleep(c.FlushInterval)
				c.log("interval %v reached", c.FlushInterval)
				go c.flush()
			}
		}()
	}()

	return &Client{
		Debug:         false,
		BufferSize:    500,
		FlushInterval: 30 * time.Second,
		Key:           key,
		Endpoint:      api,
		buffer:        make([]*interface{}, 0),
	}
}

//
// Buffer an alias message
//

func (c *Client) Alias(previousId string) {
	c.bufferMessage(&alias{"Alias", previousId, timestamp()})
}

//
// Buffer a page message
//

func (c *Client) Page(name string, category string, properties interface{}) {
	c.bufferMessage(&page{"Page", name, category, properties, timestamp()})
}

//
// Buffer a screen message
//

func (c *Client) Screen(name string, category string, properties interface{}) {
	c.bufferMessage(&page{"Screen", name, category, properties, timestamp()})
}

//
// Buffer a group message
//

func (c *Client) Group(id string, traits interface{}) {
	c.bufferMessage(&group{"Group", id, traits, timestamp()})
}

//
// Buffer an identify message
//

func (c *Client) Identify(traits interface{}) {
	c.bufferMessage(&identify{"Identify", traits, timestamp()})
}

//
// Buffer a track message
//

func (c *Client) Track(event string, properties interface{}) {
	c.bufferMessage(&track{"Track", event, properties, timestamp()})
}

//
// Return formatted timestamp.
//

func timestamp() string {
	return strftime.Format("%Y-%m-%dT%H:%M:%S%z", time.Now())
}

//
// Log in debug mode.
//

func (c *Client) log(format string, v ...interface{}) {
	if c.Debug {
		log.Printf(format, v...)
	}
}

//
// Buffer the given message and flush
// when the buffer exceeds .BufferSize.
//

func (c *Client) bufferMessage(msg interface{}) {
	c.buffer = append(c.buffer, &msg)

	c.log("buffer (%d/%d) %v", len(c.buffer), c.BufferSize, msg)

	if len(c.buffer) >= c.BufferSize {
		go c.flush()
	}
}

// Return a batch message primed
// with context properties
//

func batchMessage(msgs []*interface{}) (*batch, error) {
	uid, err := uuid.NewV4()

	if err != nil {
		return nil, err
	}

	batch := &batch{
		RequestId: uid.String(),
		Messages:  msgs,
		Context: context{
			Library: contextLibrary{
				Name:    "analytics-go",
				Version: Version,
			},
		},
	}

	return batch, nil
}

//
// Flush the buffered messages.
//

func (c *Client) flush() error {
	if len(c.buffer) == 0 {
		c.log("no messages to flush")
		return nil
	}

	c.log("flushing %d messages", len(c.buffer))
	batch, err := batchMessage(c.buffer)

	if err != nil {
		return err
	}

	json, err := Marshal(batch)

	if err != nil {
		return err
	}

	c.buffer = nil

	client := &http.Client{}
	url := c.Endpoint + "/v1/batch"
	c.log("request %s with %d bytes", url, len(json))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(json))

	if err != nil {
		return err
	}

	req.Header.Add("User-Agent", "analytics-go (version: "+Version+")")
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Content-Length", string(len(json)))
	req.SetBasicAuth(c.Key, "")

	res, err := client.Do(req)
	c.log("response %v", res)

	return err
}
<commit_msg>fix endpoint, /v1/import not /v1/batch<commit_after>package analytics

//
// dependencies
//

import "github.com/jehiah/go-strftime"
import "github.com/nu7hatch/gouuid"
import . "encoding/json"
import "io/ioutil"
import "net/http"
import "bytes"
import "time"
import "log"

//
// Library version
//

const Version = "0.0.1"

//
// Default API end-point
//

const api = "https://api.segment.io"

//
// Segment.io client
//

type Client struct {
	Debug         bool
	BufferSize    int
	FlushInterval time.Duration
	Endpoint      string
	Key           string
	buffer        []*interface{}
}

//
// Message context library
//

type contextLibrary struct {
	Name    string `json:"name"`
	Version string `json:"version"`
}

//
// Message context
//

type context struct {
	Library contextLibrary `json:"library"`
}

//
// Identify message
//

type identify struct {
	Action    string      `json:"action"`
	Traits    interface{} `json:"trailts"`
	Timestamp string      `json:"timestamp"`
}

//
// Alias message
//

type alias struct {
	Action     string `json:"action"`
	PreviousId string `json:"previousId"`
	Timestamp  string `json:"timestamp"`
}

//
// Track message
//

type track struct {
	Action     string      `json:"action"`
	Event      string      `json:"event"`
	Properties interface{} `json:"properties"`
	Timestamp  string      `json:"timestamp"`
}

//
// Group message
//

type group struct {
	Action    string      `json:"action"`
	GroupId   string      `json:"groupId"`
	Traits    interface{} `json:"trailts"`
	Timestamp string      `json:"timestamp"`
}

//
// Page message
//

type page struct {
	Action     string      `json:"action"`
	Category   string      `json:"category"`
	Name       string      `json:"name"`
	Properties interface{} `json:"properties"`
	Timestamp  string      `json:"timestamp"`
}

//
// Batch message
//

type batch struct {
	Context   context        `json:"context"`
	RequestId string         `json:"requestId"`
	Messages  []*interface{} `json:"batch"`
}

//
// Return a new Segment.io client
// with the given write key.
//

func New(key string) (c *Client) {
	defer func() {
		go func() {
			for {
				time.Sleep(c.FlushInterval)
				c.log("interval %v reached", c.FlushInterval)
				go c.flush()
			}
		}()
	}()

	return &Client{
		Debug:         false,
		BufferSize:    500,
		FlushInterval: 30 * time.Second,
		Key:           key,
		Endpoint:      api,
		buffer:        make([]*interface{}, 0),
	}
}

//
// Buffer an alias message
//

func (c *Client) Alias(previousId string) {
	c.bufferMessage(&alias{"Alias", previousId, timestamp()})
}

//
// Buffer a page message
//

func (c *Client) Page(name string, category string, properties interface{}) {
	c.bufferMessage(&page{"Page", name, category, properties, timestamp()})
}

//
// Buffer a screen message
//

func (c *Client) Screen(name string, category string, properties interface{}) {
	c.bufferMessage(&page{"Screen", name, category, properties, timestamp()})
}

//
// Buffer a group message
//

func (c *Client) Group(id string, traits interface{}) {
	c.bufferMessage(&group{"Group", id, traits, timestamp()})
}

//
// Buffer an identify message
//

func (c *Client) Identify(traits interface{}) {
	c.bufferMessage(&identify{"Identify", traits, timestamp()})
}

//
// Buffer a track message
//

func (c *Client) Track(event string, properties interface{}) {
	c.bufferMessage(&track{"Track", event, properties, timestamp()})
}

//
// Return formatted timestamp.
//

func timestamp() string {
	return strftime.Format("%Y-%m-%dT%H:%M:%S%z", time.Now())
}

//
// Log in debug mode.
//

func (c *Client) log(format string, v ...interface{}) {
	if c.Debug {
		log.Printf(format, v...)
	}
}

//
// Buffer the given message and flush
// when the buffer exceeds .BufferSize.
//

func (c *Client) bufferMessage(msg interface{}) {
	c.buffer = append(c.buffer, &msg)

	c.log("buffer (%d/%d) %v", len(c.buffer), c.BufferSize, msg)

	if len(c.buffer) >= c.BufferSize {
		go c.flush()
	}
}

// Return a batch message primed
// with context properties
//

func batchMessage(msgs []*interface{}) (*batch, error) {
	uid, err := uuid.NewV4()

	if err != nil {
		return nil, err
	}

	batch := &batch{
		RequestId: uid.String(),
		Messages:  msgs,
		Context: context{
			Library: contextLibrary{
				Name:    "analytics-go",
				Version: Version,
			},
		},
	}

	return batch, nil
}

//
// Flush the buffered messages.
//

func (c *Client) flush() error {
	if len(c.buffer) == 0 {
		c.log("no messages to flush")
		return nil
	}

	c.log("flushing %d messages", len(c.buffer))
	batch, err := batchMessage(c.buffer)

	if err != nil {
		return err
	}

	json, err := Marshal(batch)

	if err != nil {
		return err
	}

	c.buffer = nil

	client := &http.Client{}
	url := c.Endpoint + "/v1/import"
	c.log("request %s with %d bytes", url, len(json))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(json))

	if err != nil {
		return err
	}

	req.Header.Add("User-Agent", "analytics-go (version: "+Version+")")
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Content-Length", string(len(json)))
	req.SetBasicAuth(c.Key, "")

	res, err := client.Do(req)
	c.log("response %v", res)

	if res.StatusCode >= 500 {
		body, _ := ioutil.ReadAll(res.Body)
		c.log("body %s", string(body))
	}

	return err
}
<|endoftext|>


<commit_before>package analytics

import (
	"io"
	"io/ioutil"

	"bytes"
	"encoding/json"
	"net/http"
	"time"
)

// Version of the client.
const Version = "3.0.0"

// This interface is the main API exposed by the analytics package.
// Values that satsify this interface are returned by the client constructors
// provided by the package and provide a way to send messages via the HTTP API.
type Client interface {
	io.Closer

	// Queues a message to be sent by the client when the conditions for a batch
	// upload are met.
	// This is the main method you'll be using, a typical flow would look like
	// this:
	//
	//	client := analytics.New(writeKey)
	//	...
	//	client.Enqueue(analytics.Track{ ... })
	//	...
	//	client.Close()
	//
	// The method returns an error if the message queue not be queued, which
	// happens if the client was already closed at the time the method was
	// called or if the message was malformed.
	Enqueue(Message) error
}

type client struct {
	Config
	key string

	// This channel is where the `Enqueue` method writes messages so they can be
	// picked up and pushed by the backend goroutine taking care of applying the
	// batching rules.
	msgs chan Message

	// These two channels are used to synchronize the client shutting down when
	// `Close` is called.
	// The first channel is closed to signal the backend goroutine that it has
	// to stop, then the second one is closed by the backend goroutine to signal
	// that it has finished flushing all queued messages.
	quit     chan struct{}
	shutdown chan struct{}

	// This HTTP client is used to send requests to the backend, it uses the
	// HTTP transport provided in the configuration.
	http http.Client
}

// Instantiate a new client that uses the write key passed as first argument to
// send messages to the backend.
// The client is created with the default configuration.
func New(writeKey string) Client {
	// Here we can ignore the error because the default config is always valid.
	c, _ := NewWithConfig(writeKey, Config{})
	return c
}

// Instantiate a new client that uses the write key and configuration passed as
// arguments to send messages to the backend.
// The function will return an error if the configuration contained impossible
// values (like a negative flush interval for example).
// When the function returns an error the returned client will always be nil.
func NewWithConfig(writeKey string, config Config) (cli Client, err error) {
	if err = config.validate(); err != nil {
		return
	}

	c := &client{
		Config:   makeConfig(config),
		key:      writeKey,
		msgs:     make(chan Message, 100),
		quit:     make(chan struct{}),
		shutdown: make(chan struct{}),
		http: http.Client{
			Transport: config.Transport,
		},
	}

	go c.loop()

	cli = c
	return
}

func (c *client) Enqueue(msg Message) (err error) {
	if err = msg.validate(); err != nil {
		return
	}

	var id = c.uid()
	var ts = c.now()

	switch m := msg.(type) {
	case Alias:
		m.Type = "alias"
		m.MessageId = makeMessageId(m.MessageId, id)
		m.Timestamp = makeTimestamp(m.Timestamp, ts)
		msg = m

	case Group:
		m.Type = "group"
		m.MessageId = makeMessageId(m.MessageId, id)
		m.Timestamp = makeTimestamp(m.Timestamp, ts)
		msg = m

	case Identify:
		m.Type = "identify"
		m.MessageId = makeMessageId(m.MessageId, id)
		m.Timestamp = makeTimestamp(m.Timestamp, ts)
		msg = m

	case Page:
		m.Type = "page"
		m.MessageId = makeMessageId(m.MessageId, id)
		m.Timestamp = makeTimestamp(m.Timestamp, ts)
		msg = m

	case Screen:
		m.Type = "screen"
		m.MessageId = makeMessageId(m.MessageId, id)
		m.Timestamp = makeTimestamp(m.Timestamp, ts)
		msg = m

	case Track:
		m.Type = "track"
		m.MessageId = makeMessageId(m.MessageId, id)
		m.Timestamp = makeTimestamp(m.Timestamp, ts)
		msg = m
	}

	defer func() {
		// When the `msgs` channel is closed writing to it will trigger a panic.
		// To avoid letting the panic propagate to the caller we recover from it
		// and instead report that the client has been closed and shouldn't be
		// used anymore.
		if recover() != nil {
			err = ErrClosed
		}
	}()

	c.msgs <- msg
	return
}

// Close and flush metrics.
func (c *client) Close() (err error) {
	defer func() {
		// Always recover, a panic could be raised if `c`.quit was closed which
		// means the method was called more than once.
		if recover() != nil {
			err = ErrClosed
		}
	}()
	close(c.quit)
	<-c.shutdown
	return
}

// Send batch request.
func (c *client) send(msgs []message) {
	const attempts = 10

	if len(msgs) == 0 {
		return
	}

	b, err := json.Marshal(batch{
		MessageId: c.uid(),
		SentAt:    c.now(),
		Messages:  msgs,
		Context:   c.DefaultContext,
	})

	if err != nil {
		c.errorf("marshalling mesages - %s", err)
		return
	}

	for i := 0; i != attempts; i++ {
		if err := c.upload(b); err == nil {
			return
		}

		// Wait for either a retry timeout or the client to be closed.
		select {
		case <-time.After(c.RetryAfter(i)):
		case <-c.quit:
			c.errorf("%d messages dropped because they failed to be sent and the client was closed", len(msgs))
			return
		}
	}

	c.errorf("%d messages dropped because they failed to be sent after %d attempts", len(msgs), attempts)
}

// Upload serialized batch message.
func (c *client) upload(b []byte) error {
	url := c.Endpoint + "/v1/batch"
	req, err := http.NewRequest("POST", url, bytes.NewReader(b))
	if err != nil {
		c.errorf("creating request - %s", err)
		return err
	}

	req.Header.Add("User-Agent", "analytics-go (version: "+Version+")")
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Content-Length", string(len(b)))
	req.SetBasicAuth(c.key, "")

	res, err := c.http.Do(req)

	if err != nil {
		c.errorf("sending request - %s", err)
		return err
	}

	defer res.Body.Close()
	c.report(res)

	return nil
}

// Report on response body.
func (c *client) report(res *http.Response) {
	if res.StatusCode < 400 {
		c.debugf("response %s", res.Status)
		return
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		c.errorf("reading response body - %s", err)
		return
	}

	c.logf("response %s: %d – %s", res.Status, res.StatusCode, body)
}

// Batch loop.
func (c *client) loop() {
	defer close(c.shutdown)

	tick := time.NewTicker(c.Interval)
	defer tick.Stop()

	mq := messageQueue{
		maxBatchSize:  c.BatchSize,
		maxBatchBytes: c.maxBatchBytes(),
	}

	for {
		select {
		case msg := <-c.msgs:
			c.push(&mq, msg)

		case <-tick.C:
			c.flush(&mq)

		case <-c.quit:
			c.debugf("exit requested – draining messages")

			// Drain the msg channel, we have to close it first so no more
			// messages can be pushed and otherwise the loop would never end.
			close(c.msgs)
			for msg := range c.msgs {
				c.push(&mq, msg)
			}

			c.flush(&mq)
			c.debugf("exit")
			return
		}
	}
}

func (c *client) push(q *messageQueue, m Message) {
	var msg message
	var err error

	if msg, err = makeMessage(m, maxMessageBytes); err != nil {
		c.errorf("%s - %v", err, m)
		return
	}

	c.debugf("buffer (%d/%d) %v", len(q.pending), c.BatchSize, m)

	if msgs := q.push(msg); msgs != nil {
		c.debugf("exceeded messages batch limit with batch of %d messages – flushing", len(msgs))
		c.send(msgs)
	}
}

func (c *client) flush(q *messageQueue) {
	if msgs := q.flush(); msgs != nil {
		c.debugf("flushing %d messages", len(msgs))
		c.send(msgs)
	}
}

func (c *client) debugf(format string, args ...interface{}) {
	if c.Verbose {
		c.logf(format, args...)
	}
}

func (c *client) logf(format string, args ...interface{}) {
	c.Logger.Logf(format, args...)
}

func (c *client) errorf(format string, args ...interface{}) {
	c.Logger.Errorf(format, args...)
}

func (c *client) maxBatchBytes() int {
	b, _ := json.Marshal(batch{
		MessageId: c.uid(),
		SentAt:    c.now(),
		Context:   c.DefaultContext,
	})
	return maxBatchBytes - len(b)
}
<commit_msg>cleanup<commit_after>package analytics

import (
	"io"
	"io/ioutil"

	"bytes"
	"encoding/json"
	"net/http"
	"time"
)

// Version of the client.
const Version = "3.0.0"

// This interface is the main API exposed by the analytics package.
// Values that satsify this interface are returned by the client constructors
// provided by the package and provide a way to send messages via the HTTP API.
type Client interface {
	io.Closer

	// Queues a message to be sent by the client when the conditions for a batch
	// upload are met.
	// This is the main method you'll be using, a typical flow would look like
	// this:
	//
	//	client := analytics.New(writeKey)
	//	...
	//	client.Enqueue(analytics.Track{ ... })
	//	...
	//	client.Close()
	//
	// The method returns an error if the message queue not be queued, which
	// happens if the client was already closed at the time the method was
	// called or if the message was malformed.
	Enqueue(Message) error
}

type client struct {
	Config
	key string

	// This channel is where the `Enqueue` method writes messages so they can be
	// picked up and pushed by the backend goroutine taking care of applying the
	// batching rules.
	msgs chan Message

	// These two channels are used to synchronize the client shutting down when
	// `Close` is called.
	// The first channel is closed to signal the backend goroutine that it has
	// to stop, then the second one is closed by the backend goroutine to signal
	// that it has finished flushing all queued messages.
	quit     chan struct{}
	shutdown chan struct{}

	// This HTTP client is used to send requests to the backend, it uses the
	// HTTP transport provided in the configuration.
	http http.Client
}

// Instantiate a new client that uses the write key passed as first argument to
// send messages to the backend.
// The client is created with the default configuration.
func New(writeKey string) Client {
	// Here we can ignore the error because the default config is always valid.
	c, _ := NewWithConfig(writeKey, Config{})
	return c
}

// Instantiate a new client that uses the write key and configuration passed as
// arguments to send messages to the backend.
// The function will return an error if the configuration contained impossible
// values (like a negative flush interval for example).
// When the function returns an error the returned client will always be nil.
func NewWithConfig(writeKey string, config Config) (cli Client, err error) {
	if err = config.validate(); err != nil {
		return
	}

	c := &client{
		Config:   makeConfig(config),
		key:      writeKey,
		msgs:     make(chan Message, 100),
		quit:     make(chan struct{}),
		shutdown: make(chan struct{}),
		http: http.Client{
			Transport: config.Transport,
		},
	}

	go c.loop()

	cli = c
	return
}

func (c *client) Enqueue(msg Message) (err error) {
	if err = msg.validate(); err != nil {
		return
	}

	var id = c.uid()
	var ts = c.now()

	switch m := msg.(type) {
	case Alias:
		m.Type = "alias"
		m.MessageId = makeMessageId(m.MessageId, id)
		m.Timestamp = makeTimestamp(m.Timestamp, ts)
		msg = m

	case Group:
		m.Type = "group"
		m.MessageId = makeMessageId(m.MessageId, id)
		m.Timestamp = makeTimestamp(m.Timestamp, ts)
		msg = m

	case Identify:
		m.Type = "identify"
		m.MessageId = makeMessageId(m.MessageId, id)
		m.Timestamp = makeTimestamp(m.Timestamp, ts)
		msg = m

	case Page:
		m.Type = "page"
		m.MessageId = makeMessageId(m.MessageId, id)
		m.Timestamp = makeTimestamp(m.Timestamp, ts)
		msg = m

	case Screen:
		m.Type = "screen"
		m.MessageId = makeMessageId(m.MessageId, id)
		m.Timestamp = makeTimestamp(m.Timestamp, ts)
		msg = m

	case Track:
		m.Type = "track"
		m.MessageId = makeMessageId(m.MessageId, id)
		m.Timestamp = makeTimestamp(m.Timestamp, ts)
		msg = m
	}

	defer func() {
		// When the `msgs` channel is closed writing to it will trigger a panic.
		// To avoid letting the panic propagate to the caller we recover from it
		// and instead report that the client has been closed and shouldn't be
		// used anymore.
		if recover() != nil {
			err = ErrClosed
		}
	}()

	c.msgs <- msg
	return
}

// Close and flush metrics.
func (c *client) Close() (err error) {
	defer func() {
		// Always recover, a panic could be raised if `c`.quit was closed which
		// means the method was called more than once.
		if recover() != nil {
			err = ErrClosed
		}
	}()
	close(c.quit)
	<-c.shutdown
	return
}

// Send batch request.
func (c *client) send(msgs []message) {
	const attempts = 10

	b, err := json.Marshal(batch{
		MessageId: c.uid(),
		SentAt:    c.now(),
		Messages:  msgs,
		Context:   c.DefaultContext,
	})

	if err != nil {
		c.errorf("marshalling mesages - %s", err)
		return
	}

	for i := 0; i != attempts; i++ {
		if err := c.upload(b); err == nil {
			return
		}

		// Wait for either a retry timeout or the client to be closed.
		select {
		case <-time.After(c.RetryAfter(i)):
		case <-c.quit:
			c.errorf("%d messages dropped because they failed to be sent and the client was closed", len(msgs))
			return
		}
	}

	c.errorf("%d messages dropped because they failed to be sent after %d attempts", len(msgs), attempts)
}

// Upload serialized batch message.
func (c *client) upload(b []byte) error {
	url := c.Endpoint + "/v1/batch"
	req, err := http.NewRequest("POST", url, bytes.NewReader(b))
	if err != nil {
		c.errorf("creating request - %s", err)
		return err
	}

	req.Header.Add("User-Agent", "analytics-go (version: "+Version+")")
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Content-Length", string(len(b)))
	req.SetBasicAuth(c.key, "")

	res, err := c.http.Do(req)

	if err != nil {
		c.errorf("sending request - %s", err)
		return err
	}

	defer res.Body.Close()
	c.report(res)

	return nil
}

// Report on response body.
func (c *client) report(res *http.Response) {
	if res.StatusCode < 400 {
		c.debugf("response %s", res.Status)
		return
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		c.errorf("reading response body - %s", err)
		return
	}

	c.logf("response %s: %d – %s", res.Status, res.StatusCode, body)
}

// Batch loop.
func (c *client) loop() {
	defer close(c.shutdown)

	tick := time.NewTicker(c.Interval)
	defer tick.Stop()

	mq := messageQueue{
		maxBatchSize:  c.BatchSize,
		maxBatchBytes: c.maxBatchBytes(),
	}

	for {
		select {
		case msg := <-c.msgs:
			c.push(&mq, msg)

		case <-tick.C:
			c.flush(&mq)

		case <-c.quit:
			c.debugf("exit requested – draining messages")

			// Drain the msg channel, we have to close it first so no more
			// messages can be pushed and otherwise the loop would never end.
			close(c.msgs)
			for msg := range c.msgs {
				c.push(&mq, msg)
			}

			c.flush(&mq)
			c.debugf("exit")
			return
		}
	}
}

func (c *client) push(q *messageQueue, m Message) {
	var msg message
	var err error

	if msg, err = makeMessage(m, maxMessageBytes); err != nil {
		c.errorf("%s - %v", err, m)
		return
	}

	c.debugf("buffer (%d/%d) %v", len(q.pending), c.BatchSize, m)

	if msgs := q.push(msg); msgs != nil {
		c.debugf("exceeded messages batch limit with batch of %d messages – flushing", len(msgs))
		c.send(msgs)
	}
}

func (c *client) flush(q *messageQueue) {
	if msgs := q.flush(); msgs != nil {
		c.debugf("flushing %d messages", len(msgs))
		c.send(msgs)
	}
}

func (c *client) debugf(format string, args ...interface{}) {
	if c.Verbose {
		c.logf(format, args...)
	}
}

func (c *client) logf(format string, args ...interface{}) {
	c.Logger.Logf(format, args...)
}

func (c *client) errorf(format string, args ...interface{}) {
	c.Logger.Errorf(format, args...)
}

func (c *client) maxBatchBytes() int {
	b, _ := json.Marshal(batch{
		MessageId: c.uid(),
		SentAt:    c.now(),
		Context:   c.DefaultContext,
	})
	return maxBatchBytes - len(b)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/dghubble/sling"
	"github.com/dickeyxxx/golock"
)

var analyticsPath = filepath.Join(CacheHome, "analytics.json")

type analyticsBody struct {
	Schema   int                `json:"schema"`
	Commands []AnalyticsCommand `json:"commands"`
	User     string             `json:"user,omitempty"`
}

var currentAnalyticsCommand = &AnalyticsCommand{
	Timestamp: time.Now().Unix(),
	OS:        runtime.GOOS,
	Arch:      runtime.GOARCH,
	Language:  "go",
	Valid:     true,
}

// AnalyticsCommand represents an analytics command
type AnalyticsCommand struct {
	Command       string `json:"command"`
	Plugin        string `json:"plugin,omitempty"`
	PluginVersion string `json:"plugin_version,omitempty"`
	Timestamp     int64  `json:"timestamp"`
	Version       string `json:"version"`
	OS            string `json:"os"`
	Arch          string `json:"arch"`
	Language      string `json:"language"`
	Status        int    `json:"status"`
	Runtime       int64  `json:"runtime"`
	Valid         bool   `json:"valid"`
	start         time.Time
}

// RecordStart marks when a command was started (for tracking runtime)
func (c *AnalyticsCommand) RecordStart() {
	c.Version = Version
	c.start = time.Now()
}

// RecordEnd marks when a command was completed
// and records it to the analytics file
func (c *AnalyticsCommand) RecordEnd(status int) {
	if c == nil || skipAnalytics() || len(Args) < 2 || (c.Valid && c.start.IsZero()) {
		return
	}
	c.Command = Args[1]
	c.Status = status
	if !c.start.IsZero() {
		c.Runtime = (time.Now().UnixNano() - c.start.UnixNano()) / 1000000
	}
	file := readAnalyticsFile()
	file.Commands = append(file.Commands, *c)
	LogIfError(writeAnalyticsFile(file))
}

func readAnalyticsFile() (file analyticsBody) {
	f, err := os.Open(analyticsPath)
	if err != nil {
		if !os.IsNotExist(err) {
			LogIfError(err)
		}
		return
	}
	if err := json.NewDecoder(f).Decode(&file); err != nil {
		LogIfError(err)
	}
	if file.Schema != 1 {
		return analyticsBody{Schema: 1}
	}
	return file
}

func writeAnalyticsFile(file analyticsBody) error {
	data, err := json.MarshalIndent(file, "", "  ")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(analyticsPath, data, 0644)
}

// SubmitAnalytics sends the analytics info to the analytics service
func SubmitAnalytics() {
	if skipAnalytics() {
		return
	}
	file := readAnalyticsFile()
	if len(file.Commands) < 10 {
		// do not record if less than 10 commands
		return
	}
	lockfile := filepath.Join(CacheHome, "analytics.lock")
	golock.Lock(lockfile)
	defer golock.Unlock(lockfile)
	file = readAnalyticsFile() // read commands again in case it was locked
	file.User = netrcLogin()

	host := os.Getenv("HEROKU_ANALYTICS_HOST")
	if host == "" {
		host = "https://cli-analytics.heroku.com"
	}

	resp, err := sling.New().Base(host).Post("/record").BodyJSON(file).ReceiveSuccess(nil)
	if err != nil {
		LogIfError(err)
		return
	}
	LogIfError(getHTTPError(resp))
	writeAnalyticsFile(analyticsBody{Schema: 1})
}

func skipAnalytics() bool {
	return os.Getenv("TESTING") == ONE || (config.SkipAnalytics != nil && *config.SkipAnalytics) || netrcLogin() == ""
}
<commit_msg>submit analytics even when <10 commands since it happens during updates<commit_after>package main

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/dghubble/sling"
	"github.com/dickeyxxx/golock"
)

var analyticsPath = filepath.Join(CacheHome, "analytics.json")

type analyticsBody struct {
	Schema   int                `json:"schema"`
	Commands []AnalyticsCommand `json:"commands"`
	User     string             `json:"user,omitempty"`
}

var currentAnalyticsCommand = &AnalyticsCommand{
	Timestamp: time.Now().Unix(),
	OS:        runtime.GOOS,
	Arch:      runtime.GOARCH,
	Language:  "go",
	Valid:     true,
}

// AnalyticsCommand represents an analytics command
type AnalyticsCommand struct {
	Command       string `json:"command"`
	Plugin        string `json:"plugin,omitempty"`
	PluginVersion string `json:"plugin_version,omitempty"`
	Timestamp     int64  `json:"timestamp"`
	Version       string `json:"version"`
	OS            string `json:"os"`
	Arch          string `json:"arch"`
	Language      string `json:"language"`
	Status        int    `json:"status"`
	Runtime       int64  `json:"runtime"`
	Valid         bool   `json:"valid"`
	start         time.Time
}

// RecordStart marks when a command was started (for tracking runtime)
func (c *AnalyticsCommand) RecordStart() {
	c.Version = Version
	c.start = time.Now()
}

// RecordEnd marks when a command was completed
// and records it to the analytics file
func (c *AnalyticsCommand) RecordEnd(status int) {
	if c == nil || skipAnalytics() || len(Args) < 2 || (c.Valid && c.start.IsZero()) {
		return
	}
	c.Command = Args[1]
	c.Status = status
	if !c.start.IsZero() {
		c.Runtime = (time.Now().UnixNano() - c.start.UnixNano()) / 1000000
	}
	file := readAnalyticsFile()
	file.Commands = append(file.Commands, *c)
	LogIfError(writeAnalyticsFile(file))
}

func readAnalyticsFile() (file analyticsBody) {
	f, err := os.Open(analyticsPath)
	if err != nil {
		if !os.IsNotExist(err) {
			LogIfError(err)
		}
		return
	}
	if err := json.NewDecoder(f).Decode(&file); err != nil {
		LogIfError(err)
	}
	if file.Schema != 1 {
		return analyticsBody{Schema: 1}
	}
	return file
}

func writeAnalyticsFile(file analyticsBody) error {
	data, err := json.MarshalIndent(file, "", "  ")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(analyticsPath, data, 0644)
}

// SubmitAnalytics sends the analytics info to the analytics service
func SubmitAnalytics() {
	if skipAnalytics() {
		return
	}
	file := readAnalyticsFile()
	lockfile := filepath.Join(CacheHome, "analytics.lock")
	golock.Lock(lockfile)
	defer golock.Unlock(lockfile)
	file = readAnalyticsFile() // read commands again in case it was locked
	file.User = netrcLogin()

	host := os.Getenv("HEROKU_ANALYTICS_HOST")
	if host == "" {
		host = "https://cli-analytics.heroku.com"
	}

	resp, err := sling.New().Base(host).Post("/record").BodyJSON(file).ReceiveSuccess(nil)
	if err != nil {
		LogIfError(err)
		return
	}
	LogIfError(getHTTPError(resp))
	writeAnalyticsFile(analyticsBody{Schema: 1})
}

func skipAnalytics() bool {
	return os.Getenv("TESTING") == ONE || (config.SkipAnalytics != nil && *config.SkipAnalytics) || netrcLogin() == ""
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"net/http"
	"net/url"
	"time"
)

type PublicStashTabSubscriptionResult struct {
	PublicStashTabs *PublicStashTabs
	Error           error
}

type PublicStashTabSubscription struct {
	Channel      chan PublicStashTabSubscriptionResult
	closeChannel chan bool
	host         string
}

// Opens a subscription that begins with the given change id. To subscribe from the beginning, pass
// an empty string.
func OpenPublicStashTabSubscription(firstChangeId string) *PublicStashTabSubscription {
	return OpenPublicStashTabSubscriptionForHost("www.pathofexile.com", firstChangeId)
}

// Opens a subscription for an alternative host. Can be used for beta or foreign servers.
func OpenPublicStashTabSubscriptionForHost(host, firstChangeId string) *PublicStashTabSubscription {
	ret := &PublicStashTabSubscription{
		Channel:      make(chan PublicStashTabSubscriptionResult),
		closeChannel: make(chan bool),
		host:         host,
	}
	go ret.run(firstChangeId)
	return ret
}

func (s *PublicStashTabSubscription) Close() {
	s.closeChannel <- true
}

func (s *PublicStashTabSubscription) run(firstChangeId string) {
	defer close(s.Channel)

	nextChangeId := firstChangeId

	const requestInterval = time.Second
	var lastRequestTime time.Time

	for {
		waitTime := requestInterval - time.Now().Sub(lastRequestTime)
		if waitTime > 0 {
			time.Sleep(waitTime)
		}

		select {
		case <-s.closeChannel:
			return
		default:
			lastRequestTime = time.Now()
			response, err := http.Get("https://" + s.host + "/api/public-stash-tabs?id=" + url.QueryEscape(nextChangeId))
			if err != nil {
				s.Channel <- PublicStashTabSubscriptionResult{
					Error: err,
				}
				continue
			}

			tabs := new(PublicStashTabs)
			decoder := json.NewDecoder(response.Body)
			err = decoder.Decode(tabs)
			if err != nil {
				s.Channel <- PublicStashTabSubscriptionResult{
					Error: err,
				}
				continue
			}

			nextChangeId = tabs.NextChangeId

			if len(tabs.Stashes) > 0 {
				s.Channel <- PublicStashTabSubscriptionResult{
					PublicStashTabs: tabs,
				}
			}
		}
	}
}
<commit_msg>Add ChangeId to PublicStashTabSubscriptionResult (#4)<commit_after>package api

import (
	"encoding/json"
	"net/http"
	"net/url"
	"time"
)

type PublicStashTabSubscriptionResult struct {
	ChangeId        string
	PublicStashTabs *PublicStashTabs
	Error           error
}

type PublicStashTabSubscription struct {
	Channel      chan PublicStashTabSubscriptionResult
	closeChannel chan bool
	host         string
}

// Opens a subscription that begins with the given change id. To subscribe from the beginning, pass
// an empty string.
func OpenPublicStashTabSubscription(firstChangeId string) *PublicStashTabSubscription {
	return OpenPublicStashTabSubscriptionForHost("www.pathofexile.com", firstChangeId)
}

// Opens a subscription for an alternative host. Can be used for beta or foreign servers.
func OpenPublicStashTabSubscriptionForHost(host, firstChangeId string) *PublicStashTabSubscription {
	ret := &PublicStashTabSubscription{
		Channel:      make(chan PublicStashTabSubscriptionResult),
		closeChannel: make(chan bool),
		host:         host,
	}
	go ret.run(firstChangeId)
	return ret
}

func (s *PublicStashTabSubscription) Close() {
	s.closeChannel <- true
}

func (s *PublicStashTabSubscription) run(firstChangeId string) {
	defer close(s.Channel)

	nextChangeId := firstChangeId

	const requestInterval = time.Second
	var lastRequestTime time.Time

	for {
		waitTime := requestInterval - time.Now().Sub(lastRequestTime)
		if waitTime > 0 {
			time.Sleep(waitTime)
		}

		select {
		case <-s.closeChannel:
			return
		default:
			lastRequestTime = time.Now()
			response, err := http.Get("https://" + s.host + "/api/public-stash-tabs?id=" + url.QueryEscape(nextChangeId))
			if err != nil {
				s.Channel <- PublicStashTabSubscriptionResult{
					ChangeId: nextChangeId,
					Error:    err,
				}
				continue
			}

			tabs := new(PublicStashTabs)
			decoder := json.NewDecoder(response.Body)
			err = decoder.Decode(tabs)
			if err != nil {
				s.Channel <- PublicStashTabSubscriptionResult{
					ChangeId: nextChangeId,
					Error:    err,
				}
				continue
			}

			if len(tabs.Stashes) > 0 {
				s.Channel <- PublicStashTabSubscriptionResult{
					ChangeId:        nextChangeId,
					PublicStashTabs: tabs,
				}
			}

			nextChangeId = tabs.NextChangeId
		}
	}
}
<|endoftext|>


<commit_before>package openstack

import (
	"os"
	"testing"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
)

var testAccProviders map[string]terraform.ResourceProvider
var testAccProvider *schema.Provider

func init() {
	testAccProvider = Provider().(*schema.Provider)
	testAccProviders = map[string]terraform.ResourceProvider{
		"openstack": testAccProvider,
	}
}

func TestProvider(t *testing.T) {
	if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
		t.Fatalf("err: %s", err)
	}
}

func TestProvider_impl(t *testing.T) {
	var _ terraform.ResourceProvider = Provider()
}

func testAccPreCheck(t *testing.T) {
	if v := os.Getenv("OS_REGION_NAME"); v == "" {
		t.Fatal("OS_REGION_NAME must be set for acceptance tests")
	}

	if v := os.Getenv("OS_AUTH_URL"); v == "" {
		t.Fatal("OS_AUTH_URL must be set for acceptance tests")
	}

	if v := os.Getenv("OS_USERNAME"); v == "" {
		t.Fatal("OS_USERNAME must be set for acceptance tests")
	}

	if v := os.Getenv("OS_TENANT_NAME"); v != "us-central1" {
		t.Fatal("OS_TENANT_NAME must be set to us-central1 for acceptance tests")
	}

	if v := os.Getenv("OS_PASSWORD"); v != "us-central1" {
		t.Fatal("OS_PASSWORD must be set to us-central1 for acceptance tests")
	}
}
<commit_msg>add image_ref and flavor_ref checks<commit_after>package openstack

import (
	"os"
	"testing"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
)

var (
	OS_REGION_NAME = ""
)

var testAccProviders map[string]terraform.ResourceProvider
var testAccProvider *schema.Provider

func init() {
	testAccProvider = Provider().(*schema.Provider)
	testAccProviders = map[string]terraform.ResourceProvider{
		"openstack": testAccProvider,
	}
}

func TestProvider(t *testing.T) {
	if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
		t.Fatalf("err: %s", err)
	}
}

func TestProvider_impl(t *testing.T) {
	var _ terraform.ResourceProvider = Provider()
}

func testAccPreCheck(t *testing.T) {
	v := os.Getenv("OS_AUTH_URL")
	if v == "" {
		t.Fatal("OS_AUTH_URL must be set for acceptance tests")
	}

	v = os.Getenv("OS_REGION_NAME")
	if v == "" {
		t.Fatal("OS_REGION_NAME must be set for acceptance tests")
	}
	OS_REGION_NAME = v

	v = os.Getenv("OS_IMAGE_ID")
	if v == "" {
		t.Fatal("OS_IMAGE_ID must be set for acceptance tests")
	}

	v = os.Getenv("OS_FLAVOR_ID")
	if v == "" {
		t.Fatal("OS_FLAVOR_ID must be set for acceptance tests")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"cuckood"
	"cuckood/cucache/text"
	"encoding/binary"
	"flag"
	"fmt"
	"io"
	"net"
	"os"
	"os/signal"
	"runtime/pprof"
	"strconv"
	"sync"
	"syscall"

	gomem "github.com/dustin/gomemcached"
)

var reqP sync.Pool
var resP sync.Pool

func init() {
	reqP.New = func() interface{} {
		return new(gomem.MCRequest)
	}
	resP.New = func() interface{} {
		return new(gomem.MCResponse)
	}
}

func main() {
	cpuprofile := flag.String("cpuprofile", "", "CPU profile output file")
	port := flag.Int("p", 11211, "TCP port to listen on")
	udpport := flag.Int("U", 11211, "UDP port to listen on")
	flag.Parse()

	c := cuckoo.New()

	var pf *os.File
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, os.Interrupt, syscall.SIGABRT)
	go func() {
		for s := range sigs {
			if pf != nil {
				pprof.StopCPUProfile()
				err := pf.Close()
				if err != nil {
					fmt.Println("could not end cpu profile:", err)
				}
			}
			if s == os.Interrupt {
				os.Exit(0)
			}
		}
	}()

	var err error
	if cpuprofile != nil && *cpuprofile != "" {
		fmt.Println("starting CPU profiling")
		pf, err = os.Create(*cpuprofile)
		if err != nil {
			fmt.Printf("could not create CPU profile file %v: %v\n", *cpuprofile, err)
			return
		}
		err = pprof.StartCPUProfile(pf)
		if err != nil {
			fmt.Printf("could not start CPU profiling: %v\n", err)
			return
		}
	}

	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		ln, err := net.Listen("tcp", ":"+strconv.Itoa(*port))
		if err != nil {
			panic(err)
		}
		for {
			conn, err := ln.Accept()
			if err != nil {
				fmt.Println(err)
				continue
			}
			go handleConnection(c, conn)
		}
	}()
	wg.Add(1)
	go func() {
		defer wg.Done()
		ln, err := net.ListenPacket("udp", ":"+strconv.Itoa(*udpport))
		if err != nil {
			panic(err)
		}
		for {
			b := make([]byte, 0, 10240)
			_, addr, err := ln.ReadFrom(b)
			if err != nil {
				fmt.Println(err)
				continue
			}
			go replyTo(c, b, addr.(*net.UDPAddr))
		}
	}()
	wg.Wait()
}

func wtf(req *gomem.MCRequest, v cuckoo.MemopRes) {
	panic(fmt.Sprintf("unexpected result when handling %v: %v\n", req.Opcode, v))
}

func execute(c cuckoo.Cuckoo, in <-chan *gomem.MCRequest, out chan<- *gomem.MCResponse) {
	mx := new(sync.Mutex)

	for req := range in {
		res := req2res(c, req)
		if req.Opcode.IsQuiet() && res.Status == gomem.SUCCESS {
			if req.Opcode == gomem.GETQ || req.Opcode == gomem.GETKQ {
				// simply don't flush
			} else {
				continue
			}
		}

		if (req.Opcode == gomem.GETQ || req.Opcode == gomem.GETKQ) && res.Status == gomem.KEY_ENOENT {
			// no warning on cache miss
			continue
		}

		if res.Status != gomem.SUCCESS {
			if !(res.Status == gomem.KEY_ENOENT && (req.Opcode == gomem.GET || req.Opcode == gomem.GETK)) {
				fmt.Println(req.Opcode, res.Status)
			}
		}

		reqP.Put(req)
		mx.Lock()
		go func() {
			out <- res
			mx.Unlock()
		}()
	}
	close(out)
}

func writeback(in <-chan *gomem.MCResponse, out_ io.Writer) {
	out := bufio.NewWriter(out_)
	mx := new(sync.Mutex)

	for res := range in {
		if res.Opaque != 0xffffffff {
			// binary protocol
			quiet := res.Opcode.IsQuiet()
			b := res.Bytes()
			resP.Put(res)

			mx.Lock()
			out.Write(b)

			// "The getq command is both mum on cache miss and quiet,
			// holding its response until a non-quiet command is issued."
			if !quiet {
				// This allows us to do Bytes() and Flush() in
				// parallel
				go func() {
					out.Flush()
					mx.Unlock()
				}()
			} else {
				mx.Unlock()
			}
			continue
		}

		// we've got a text protocol client
		if res.Opcode.IsQuiet() && res.Status == gomem.SUCCESS {
			// there is absolutely no reason to reply here
			// a noreply get doesn't exist in the text protocol
			resP.Put(res)
			continue
		}

		// TODO: return when writes fail
		switch res.Status {
		case gomem.SUCCESS:
			switch res.Opcode {
			case gomem.GETK:
				flags := binary.BigEndian.Uint32(res.Extras[0:4])
				out.Write([]byte(fmt.Sprintf("VALUE %s %d %d %d\r\n", res.Key, flags, len(res.Body), res.Cas)))
				out.Write(res.Body)
				out.Write([]byte{'\r', '\n'})
				out.Write([]byte("END\r\n"))
			case gomem.SET, gomem.ADD, gomem.REPLACE:
				out.Write([]byte("STORED\r\n"))
			case gomem.DELETE:
				out.Write([]byte("DELETED\r\n"))
			case gomem.INCREMENT, gomem.DECREMENT:
				v := binary.BigEndian.Uint64(res.Body)
				out.Write([]byte(strconv.FormatUint(v, 10) + "\r\n"))
			}
		case gomem.KEY_ENOENT:
			out.Write([]byte("NOT_FOUND\r\n"))
		case gomem.KEY_EEXISTS:
			out.Write([]byte("EXISTS\r\n"))
		case gomem.NOT_STORED:
			out.Write([]byte("NOT_STORED\r\n"))
		case gomem.ENOMEM:
			out.Write([]byte("SERVER_ERROR no space for new entry\r\n"))
		case gomem.DELTA_BADVAL:
			out.Write([]byte("CLIENT_ERROR incr/decr on non-numeric field\r\n"))
		case gomem.UNKNOWN_COMMAND:
			out.Write([]byte("ERROR\r\n"))
		}
		resP.Put(res)
	}
}

func parse(in_ io.Reader, out chan<- *gomem.MCRequest) {
	in := bufio.NewReader(in_)

	for {
		b, err := in.Peek(1)
		if err != nil {
			if err == io.EOF {
				return
			}
			// TODO print error
			return
		}

		req := reqP.Get().(*gomem.MCRequest)
		req.Cas = 0
		req.Key = nil
		req.Body = nil
		req.Extras = nil
		req.Opcode = 0
		req.Opaque = 0
		if b[0] == gomem.REQ_MAGIC {
			_, err := req.Receive(in, nil)
			if err != nil {
				if err == io.EOF {
					reqP.Put(req)
					return
				}
				// TODO: print error
				continue
			}
		} else {
			// text protocol fallback
			cmd, err := in.ReadString('\n')
			if err != nil {
				if err == io.EOF {
					reqP.Put(req)
					return
				}
				// TODO: print error
				return
			}

			*req, err = text.ToMCRequest(cmd, in)
			req.Opaque = 0xffffffff
		}

		out <- req
	}
	close(out)
}

func setup(c cuckoo.Cuckoo, in io.Reader, out io.Writer) {
	dispatch := make(chan *gomem.MCRequest, 50)
	bridge := make(chan *gomem.MCResponse, 50)
	go execute(c, dispatch, bridge)
	go writeback(bridge, out)
	parse(in, dispatch)
}

func replyTo(c cuckoo.Cuckoo, in []byte, to *net.UDPAddr) {
	u, err := net.ListenPacket("udp", "127.0.0.1:0")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer u.Close()

	var o bytes.Buffer
	setup(c, bytes.NewBuffer(in), &o)
	_, err = u.WriteTo(o.Bytes(), to)
	if err != nil {
		fmt.Println(err)
	}
}

func handleConnection(c cuckoo.Cuckoo, conn net.Conn) {
	setup(c, conn, conn)
	conn.Close()
}
<commit_msg>Handle SIGTERM<commit_after>package main

import (
	"bufio"
	"bytes"
	"cuckood"
	"cuckood/cucache/text"
	"encoding/binary"
	"flag"
	"fmt"
	"io"
	"net"
	"os"
	"os/signal"
	"runtime/pprof"
	"strconv"
	"sync"
	"syscall"

	gomem "github.com/dustin/gomemcached"
)

var reqP sync.Pool
var resP sync.Pool

func init() {
	reqP.New = func() interface{} {
		return new(gomem.MCRequest)
	}
	resP.New = func() interface{} {
		return new(gomem.MCResponse)
	}
}

func main() {
	cpuprofile := flag.String("cpuprofile", "", "CPU profile output file")
	port := flag.Int("p", 11211, "TCP port to listen on")
	udpport := flag.Int("U", 11211, "UDP port to listen on")
	flag.Parse()

	c := cuckoo.New()

	var pf *os.File
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGABRT)
	go func() {
		for s := range sigs {
			if pf != nil {
				pprof.StopCPUProfile()
				err := pf.Close()
				if err != nil {
					fmt.Println("could not end cpu profile:", err)
				}
			}
			if s == os.Interrupt {
				os.Exit(0)
			}
		}
	}()

	var err error
	if cpuprofile != nil && *cpuprofile != "" {
		fmt.Println("starting CPU profiling")
		pf, err = os.Create(*cpuprofile)
		if err != nil {
			fmt.Printf("could not create CPU profile file %v: %v\n", *cpuprofile, err)
			return
		}
		err = pprof.StartCPUProfile(pf)
		if err != nil {
			fmt.Printf("could not start CPU profiling: %v\n", err)
			return
		}
	}

	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		ln, err := net.Listen("tcp", ":"+strconv.Itoa(*port))
		if err != nil {
			panic(err)
		}
		for {
			conn, err := ln.Accept()
			if err != nil {
				fmt.Println(err)
				continue
			}
			go handleConnection(c, conn)
		}
	}()
	wg.Add(1)
	go func() {
		defer wg.Done()
		ln, err := net.ListenPacket("udp", ":"+strconv.Itoa(*udpport))
		if err != nil {
			panic(err)
		}
		for {
			b := make([]byte, 0, 10240)
			_, addr, err := ln.ReadFrom(b)
			if err != nil {
				fmt.Println(err)
				continue
			}
			go replyTo(c, b, addr.(*net.UDPAddr))
		}
	}()
	wg.Wait()
}

func wtf(req *gomem.MCRequest, v cuckoo.MemopRes) {
	panic(fmt.Sprintf("unexpected result when handling %v: %v\n", req.Opcode, v))
}

func execute(c cuckoo.Cuckoo, in <-chan *gomem.MCRequest, out chan<- *gomem.MCResponse) {
	mx := new(sync.Mutex)

	for req := range in {
		res := req2res(c, req)
		if req.Opcode.IsQuiet() && res.Status == gomem.SUCCESS {
			if req.Opcode == gomem.GETQ || req.Opcode == gomem.GETKQ {
				// simply don't flush
			} else {
				continue
			}
		}

		if (req.Opcode == gomem.GETQ || req.Opcode == gomem.GETKQ) && res.Status == gomem.KEY_ENOENT {
			// no warning on cache miss
			continue
		}

		if res.Status != gomem.SUCCESS {
			if !(res.Status == gomem.KEY_ENOENT && (req.Opcode == gomem.GET || req.Opcode == gomem.GETK)) {
				fmt.Println(req.Opcode, res.Status)
			}
		}

		reqP.Put(req)
		mx.Lock()
		go func() {
			out <- res
			mx.Unlock()
		}()
	}
	close(out)
}

func writeback(in <-chan *gomem.MCResponse, out_ io.Writer) {
	out := bufio.NewWriter(out_)
	mx := new(sync.Mutex)

	for res := range in {
		if res.Opaque != 0xffffffff {
			// binary protocol
			quiet := res.Opcode.IsQuiet()
			b := res.Bytes()
			resP.Put(res)

			mx.Lock()
			out.Write(b)

			// "The getq command is both mum on cache miss and quiet,
			// holding its response until a non-quiet command is issued."
			if !quiet {
				// This allows us to do Bytes() and Flush() in
				// parallel
				go func() {
					out.Flush()
					mx.Unlock()
				}()
			} else {
				mx.Unlock()
			}
			continue
		}

		// we've got a text protocol client
		if res.Opcode.IsQuiet() && res.Status == gomem.SUCCESS {
			// there is absolutely no reason to reply here
			// a noreply get doesn't exist in the text protocol
			resP.Put(res)
			continue
		}

		// TODO: return when writes fail
		switch res.Status {
		case gomem.SUCCESS:
			switch res.Opcode {
			case gomem.GETK:
				flags := binary.BigEndian.Uint32(res.Extras[0:4])
				out.Write([]byte(fmt.Sprintf("VALUE %s %d %d %d\r\n", res.Key, flags, len(res.Body), res.Cas)))
				out.Write(res.Body)
				out.Write([]byte{'\r', '\n'})
				out.Write([]byte("END\r\n"))
			case gomem.SET, gomem.ADD, gomem.REPLACE:
				out.Write([]byte("STORED\r\n"))
			case gomem.DELETE:
				out.Write([]byte("DELETED\r\n"))
			case gomem.INCREMENT, gomem.DECREMENT:
				v := binary.BigEndian.Uint64(res.Body)
				out.Write([]byte(strconv.FormatUint(v, 10) + "\r\n"))
			}
		case gomem.KEY_ENOENT:
			out.Write([]byte("NOT_FOUND\r\n"))
		case gomem.KEY_EEXISTS:
			out.Write([]byte("EXISTS\r\n"))
		case gomem.NOT_STORED:
			out.Write([]byte("NOT_STORED\r\n"))
		case gomem.ENOMEM:
			out.Write([]byte("SERVER_ERROR no space for new entry\r\n"))
		case gomem.DELTA_BADVAL:
			out.Write([]byte("CLIENT_ERROR incr/decr on non-numeric field\r\n"))
		case gomem.UNKNOWN_COMMAND:
			out.Write([]byte("ERROR\r\n"))
		}
		resP.Put(res)
	}
}

func parse(in_ io.Reader, out chan<- *gomem.MCRequest) {
	in := bufio.NewReader(in_)

	for {
		b, err := in.Peek(1)
		if err != nil {
			if err == io.EOF {
				return
			}
			// TODO print error
			return
		}

		req := reqP.Get().(*gomem.MCRequest)
		req.Cas = 0
		req.Key = nil
		req.Body = nil
		req.Extras = nil
		req.Opcode = 0
		req.Opaque = 0
		if b[0] == gomem.REQ_MAGIC {
			_, err := req.Receive(in, nil)
			if err != nil {
				if err == io.EOF {
					reqP.Put(req)
					return
				}
				// TODO: print error
				continue
			}
		} else {
			// text protocol fallback
			cmd, err := in.ReadString('\n')
			if err != nil {
				if err == io.EOF {
					reqP.Put(req)
					return
				}
				// TODO: print error
				return
			}

			*req, err = text.ToMCRequest(cmd, in)
			req.Opaque = 0xffffffff
		}

		out <- req
	}
	close(out)
}

func setup(c cuckoo.Cuckoo, in io.Reader, out io.Writer) {
	dispatch := make(chan *gomem.MCRequest, 50)
	bridge := make(chan *gomem.MCResponse, 50)
	go execute(c, dispatch, bridge)
	go writeback(bridge, out)
	parse(in, dispatch)
}

func replyTo(c cuckoo.Cuckoo, in []byte, to *net.UDPAddr) {
	u, err := net.ListenPacket("udp", "127.0.0.1:0")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer u.Close()

	var o bytes.Buffer
	setup(c, bytes.NewBuffer(in), &o)
	_, err = u.WriteTo(o.Bytes(), to)
	if err != nil {
		fmt.Println(err)
	}
}

func handleConnection(c cuckoo.Cuckoo, conn net.Conn) {
	setup(c, conn, conn)
	conn.Close()
}
<|endoftext|>


<commit_before>package types

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"math/big"
	"sort"
	"time"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto/sha3"
	"github.com/ethereum/go-ethereum/rlp"
)

// A BlockNonce is a 64-bit hash which proves (combined with the
// mix-hash) that a suffcient amount of computation has been carried
// out on a block.
type BlockNonce [8]byte

func EncodeNonce(i uint64) BlockNonce {
	var n BlockNonce
	binary.BigEndian.PutUint64(n[:], i)
	return n
}

func (n BlockNonce) Uint64() uint64 {
	return binary.BigEndian.Uint64(n[:])
}

type Header struct {
	ParentHash  common.Hash    // Hash to the previous block
	UncleHash   common.Hash    // Uncles of this block
	Coinbase    common.Address // The coin base address
	Root        common.Hash    // Block Trie state
	TxHash      common.Hash    // Tx sha
	ReceiptHash common.Hash    // Receipt sha
	Bloom       Bloom          // Bloom
	Difficulty  *big.Int       // Difficulty for the current block
	Number      *big.Int       // The block number
	GasLimit    *big.Int       // Gas limit
	GasUsed     *big.Int       // Gas used
	Time        uint64         // Creation time
	Extra       []byte         // Extra data
	MixDigest   common.Hash    // for quick difficulty verification
	Nonce       BlockNonce
}

func (h *Header) Hash() common.Hash {
	return rlpHash(h)
}

func (h *Header) HashNoNonce() common.Hash {
	return rlpHash([]interface{}{
		h.ParentHash,
		h.UncleHash,
		h.Coinbase,
		h.Root,
		h.TxHash,
		h.ReceiptHash,
		h.Bloom,
		h.Difficulty,
		h.Number,
		h.GasLimit,
		h.GasUsed,
		h.Time,
		h.Extra,
	})
}

func (h *Header) UnmarshalJSON(data []byte) error {
	var ext struct {
		ParentHash string
		Coinbase   string
		Difficulty string
		GasLimit   string
		Time       uint64
		Extra      string
	}
	dec := json.NewDecoder(bytes.NewReader(data))
	if err := dec.Decode(&ext); err != nil {
		return err
	}

	h.ParentHash = common.HexToHash(ext.ParentHash)
	h.Coinbase = common.HexToAddress(ext.Coinbase)
	h.Difficulty = common.String2Big(ext.Difficulty)
	h.Time = ext.Time
	h.Extra = []byte(ext.Extra)
	return nil
}

func rlpHash(x interface{}) (h common.Hash) {
	hw := sha3.NewKeccak256()
	rlp.Encode(hw, x)
	hw.Sum(h[:0])
	return h
}

type Block struct {
	header       *Header
	uncles       []*Header
	transactions Transactions
	receipts     Receipts

	Td         *big.Int
	queued     bool // flag for blockpool to skip TD check
	ReceivedAt time.Time
}

// StorageBlock defines the RLP encoding of a Block stored in the
// state database. The StorageBlock encoding contains fields that
// would otherwise need to be recomputed.
type StorageBlock Block

// "external" block encoding. used for eth protocol, etc.
type extblock struct {
	Header *Header
	Txs    []*Transaction
	Uncles []*Header
}

// "storage" block encoding. used for database.
type storageblock struct {
	Header *Header
	Txs    []*Transaction
	Uncles []*Header
	TD     *big.Int
}

var (
	emptyRootHash  = DeriveSha(Transactions{})
	emptyUncleHash = CalcUncleHash(nil)
)

// NewBlock creates a new block. The input data is copied,
// changes to header and to the field values will not affect the
// block.
//
// The values of TxHash, UncleHash, ReceiptHash and Bloom in header
// are ignored and set to values derived from the given txs, uncles
// and receipts.
func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block {
	b := &Block{header: copyHeader(header), Td: new(big.Int)}

	// TODO: panic if len(txs) != len(receipts)
	if len(txs) == 0 {
		b.header.TxHash = emptyRootHash
	} else {
		b.header.TxHash = DeriveSha(Transactions(txs))
		b.transactions = make(Transactions, len(txs))
		copy(b.transactions, txs)
	}

	if len(receipts) == 0 {
		b.header.ReceiptHash = emptyRootHash
	} else {
		b.header.ReceiptHash = DeriveSha(Receipts(receipts))
		b.header.Bloom = CreateBloom(receipts)
		b.receipts = make([]*Receipt, len(receipts))
		copy(b.receipts, receipts)
	}

	if len(uncles) == 0 {
		b.header.UncleHash = emptyUncleHash
	} else {
		b.header.UncleHash = CalcUncleHash(uncles)
		b.uncles = make([]*Header, len(uncles))
		for i := range uncles {
			b.uncles[i] = copyHeader(uncles[i])
		}
	}

	return b
}

// NewBlockWithHeader creates a block with the given header data. The
// header data is copied, changes to header and to the field values
// will not affect the block.
func NewBlockWithHeader(header *Header) *Block {
	return &Block{header: copyHeader(header)}
}

func copyHeader(h *Header) *Header {
	cpy := *h
	if cpy.Difficulty = new(big.Int); h.Difficulty != nil {
		cpy.Difficulty.Set(h.Difficulty)
	}
	if cpy.Number = new(big.Int); h.Number != nil {
		cpy.Number.Set(h.Number)
	}
	if cpy.GasLimit = new(big.Int); h.GasLimit != nil {
		cpy.GasLimit.Set(h.GasLimit)
	}
	if cpy.GasUsed = new(big.Int); h.GasUsed != nil {
		cpy.GasUsed.Set(h.GasUsed)
	}
	if len(h.Extra) > 0 {
		cpy.Extra = make([]byte, len(h.Extra))
		copy(cpy.Extra, h.Extra)
	}
	return &cpy
}

func (b *Block) ValidateFields() error {
	if b.header == nil {
		return fmt.Errorf("header is nil")
	}
	for i, transaction := range b.transactions {
		if transaction == nil {
			return fmt.Errorf("transaction %d is nil", i)
		}
	}
	for i, uncle := range b.uncles {
		if uncle == nil {
			return fmt.Errorf("uncle %d is nil", i)
		}
	}
	return nil
}

func (b *Block) DecodeRLP(s *rlp.Stream) error {
	var eb extblock
	if err := s.Decode(&eb); err != nil {
		return err
	}
	b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs
	return nil
}

func (b Block) EncodeRLP(w io.Writer) error {
	return rlp.Encode(w, extblock{
		Header: b.header,
		Txs:    b.transactions,
		Uncles: b.uncles,
	})
}

func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error {
	var sb storageblock
	if err := s.Decode(&sb); err != nil {
		return err
	}
	b.header, b.uncles, b.transactions, b.Td = sb.Header, sb.Uncles, sb.Txs, sb.TD
	return nil
}

func (b StorageBlock) EncodeRLP(w io.Writer) error {
	return rlp.Encode(w, storageblock{
		Header: b.header,
		Txs:    b.transactions,
		Uncles: b.uncles,
		TD:     b.Td,
	})
}

// TODO: copies
func (b *Block) Uncles() []*Header          { return b.uncles }
func (b *Block) Transactions() Transactions { return b.transactions }
func (b *Block) Receipts() Receipts         { return b.receipts }

func (b *Block) Transaction(hash common.Hash) *Transaction {
	for _, transaction := range b.transactions {
		if transaction.Hash() == hash {
			return transaction
		}
	}
	return nil
}

func (b *Block) Number() *big.Int     { return new(big.Int).Set(b.header.Number) }
func (b *Block) GasLimit() *big.Int   { return new(big.Int).Set(b.header.GasLimit) }
func (b *Block) GasUsed() *big.Int    { return new(big.Int).Set(b.header.GasUsed) }
func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) }

func (b *Block) NumberU64() uint64        { return b.header.Number.Uint64() }
func (b *Block) MixDigest() common.Hash   { return b.header.MixDigest }
func (b *Block) Nonce() uint64            { return binary.BigEndian.Uint64(b.header.Nonce[:]) }
func (b *Block) Bloom() Bloom             { return b.header.Bloom }
func (b *Block) Coinbase() common.Address { return b.header.Coinbase }
func (b *Block) Time() int64              { return int64(b.header.Time) }
func (b *Block) Root() common.Hash        { return b.header.Root }
func (b *Block) ParentHash() common.Hash  { return b.header.ParentHash }
func (b *Block) TxHash() common.Hash      { return b.header.TxHash }
func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash }
func (b *Block) UncleHash() common.Hash   { return b.header.UncleHash }
func (b *Block) Extra() []byte            { return common.CopyBytes(b.header.Extra) }

func (b *Block) Header() *Header { return copyHeader(b.header) }

func (b *Block) HashNoNonce() common.Hash {
	return b.header.HashNoNonce()
}

func (b *Block) Size() common.StorageSize {
	c := writeCounter(0)
	rlp.Encode(&c, b)
	return common.StorageSize(c)
}

type writeCounter common.StorageSize

func (c *writeCounter) Write(b []byte) (int, error) {
	*c += writeCounter(len(b))
	return len(b), nil
}

func CalcUncleHash(uncles []*Header) common.Hash {
	return rlpHash(uncles)
}

// WithMiningResult returns a new block with the data from b
// where nonce and mix digest are set to the provided values.
func (b *Block) WithMiningResult(nonce uint64, mixDigest common.Hash) *Block {
	cpy := *b.header
	binary.BigEndian.PutUint64(cpy.Nonce[:], nonce)
	cpy.MixDigest = mixDigest
	return &Block{
		header:       &cpy,
		transactions: b.transactions,
		receipts:     b.receipts,
		uncles:       b.uncles,
		Td:           b.Td,
	}
}

// Implement pow.Block

func (b *Block) Hash() common.Hash {
	return b.header.Hash()
}

func (b *Block) String() string {
	str := fmt.Sprintf(`Block(#%v): Size: %v TD: %v {
MinerHash: %x
%v
Transactions:
%v
Uncles:
%v
}
`, b.Number(), b.Size(), b.Td, b.header.HashNoNonce(), b.header, b.transactions, b.uncles)
	return str
}

func (h *Header) String() string {
	return fmt.Sprintf(`Header(%x):
[
	ParentHash:	    %x
	UncleHash:	    %x
	Coinbase:	    %x
	Root:		    %x
	TxSha		    %x
	ReceiptSha:	    %x
	Bloom:		    %x
	Difficulty:	    %v
	Number:		    %v
	GasLimit:	    %v
	GasUsed:	    %v
	Time:		    %v
	Extra:		    %s
	MixDigest:      %x
	Nonce:		    %x
]`, h.Hash(), h.ParentHash, h.UncleHash, h.Coinbase, h.Root, h.TxHash, h.ReceiptHash, h.Bloom, h.Difficulty, h.Number, h.GasLimit, h.GasUsed, h.Time, h.Extra, h.MixDigest, h.Nonce)
}

type Blocks []*Block

type BlockBy func(b1, b2 *Block) bool

func (self BlockBy) Sort(blocks Blocks) {
	bs := blockSorter{
		blocks: blocks,
		by:     self,
	}
	sort.Sort(bs)
}

type blockSorter struct {
	blocks Blocks
	by     func(b1, b2 *Block) bool
}

func (self blockSorter) Len() int { return len(self.blocks) }
func (self blockSorter) Swap(i, j int) {
	self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i]
}
func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) }

func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 }
<commit_msg>core/types: cache computed block values<commit_after>package types

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"math/big"
	"sort"
	"sync/atomic"
	"time"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto/sha3"
	"github.com/ethereum/go-ethereum/rlp"
)

// A BlockNonce is a 64-bit hash which proves (combined with the
// mix-hash) that a suffcient amount of computation has been carried
// out on a block.
type BlockNonce [8]byte

func EncodeNonce(i uint64) BlockNonce {
	var n BlockNonce
	binary.BigEndian.PutUint64(n[:], i)
	return n
}

func (n BlockNonce) Uint64() uint64 {
	return binary.BigEndian.Uint64(n[:])
}

type Header struct {
	ParentHash  common.Hash    // Hash to the previous block
	UncleHash   common.Hash    // Uncles of this block
	Coinbase    common.Address // The coin base address
	Root        common.Hash    // Block Trie state
	TxHash      common.Hash    // Tx sha
	ReceiptHash common.Hash    // Receipt sha
	Bloom       Bloom          // Bloom
	Difficulty  *big.Int       // Difficulty for the current block
	Number      *big.Int       // The block number
	GasLimit    *big.Int       // Gas limit
	GasUsed     *big.Int       // Gas used
	Time        uint64         // Creation time
	Extra       []byte         // Extra data
	MixDigest   common.Hash    // for quick difficulty verification
	Nonce       BlockNonce
}

func (h *Header) Hash() common.Hash {
	return rlpHash(h)
}

func (h *Header) HashNoNonce() common.Hash {
	return rlpHash([]interface{}{
		h.ParentHash,
		h.UncleHash,
		h.Coinbase,
		h.Root,
		h.TxHash,
		h.ReceiptHash,
		h.Bloom,
		h.Difficulty,
		h.Number,
		h.GasLimit,
		h.GasUsed,
		h.Time,
		h.Extra,
	})
}

func (h *Header) UnmarshalJSON(data []byte) error {
	var ext struct {
		ParentHash string
		Coinbase   string
		Difficulty string
		GasLimit   string
		Time       uint64
		Extra      string
	}
	dec := json.NewDecoder(bytes.NewReader(data))
	if err := dec.Decode(&ext); err != nil {
		return err
	}

	h.ParentHash = common.HexToHash(ext.ParentHash)
	h.Coinbase = common.HexToAddress(ext.Coinbase)
	h.Difficulty = common.String2Big(ext.Difficulty)
	h.Time = ext.Time
	h.Extra = []byte(ext.Extra)
	return nil
}

func rlpHash(x interface{}) (h common.Hash) {
	hw := sha3.NewKeccak256()
	rlp.Encode(hw, x)
	hw.Sum(h[:0])
	return h
}

type Block struct {
	header       *Header
	uncles       []*Header
	transactions Transactions
	receipts     Receipts

	// caches
	hash atomic.Value
	size atomic.Value

	// Td is used by package core to store the total difficulty
	// of the chain up to and including the block.
	Td *big.Int

	// ReceivedAt is used by package eth to track block propagation time.
	ReceivedAt time.Time
}

// StorageBlock defines the RLP encoding of a Block stored in the
// state database. The StorageBlock encoding contains fields that
// would otherwise need to be recomputed.
type StorageBlock Block

// "external" block encoding. used for eth protocol, etc.
type extblock struct {
	Header *Header
	Txs    []*Transaction
	Uncles []*Header
}

// "storage" block encoding. used for database.
type storageblock struct {
	Header *Header
	Txs    []*Transaction
	Uncles []*Header
	TD     *big.Int
}

var (
	emptyRootHash  = DeriveSha(Transactions{})
	emptyUncleHash = CalcUncleHash(nil)
)

// NewBlock creates a new block. The input data is copied,
// changes to header and to the field values will not affect the
// block.
//
// The values of TxHash, UncleHash, ReceiptHash and Bloom in header
// are ignored and set to values derived from the given txs, uncles
// and receipts.
func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block {
	b := &Block{header: copyHeader(header), Td: new(big.Int)}

	// TODO: panic if len(txs) != len(receipts)
	if len(txs) == 0 {
		b.header.TxHash = emptyRootHash
	} else {
		b.header.TxHash = DeriveSha(Transactions(txs))
		b.transactions = make(Transactions, len(txs))
		copy(b.transactions, txs)
	}

	if len(receipts) == 0 {
		b.header.ReceiptHash = emptyRootHash
	} else {
		b.header.ReceiptHash = DeriveSha(Receipts(receipts))
		b.header.Bloom = CreateBloom(receipts)
		b.receipts = make([]*Receipt, len(receipts))
		copy(b.receipts, receipts)
	}

	if len(uncles) == 0 {
		b.header.UncleHash = emptyUncleHash
	} else {
		b.header.UncleHash = CalcUncleHash(uncles)
		b.uncles = make([]*Header, len(uncles))
		for i := range uncles {
			b.uncles[i] = copyHeader(uncles[i])
		}
	}

	return b
}

// NewBlockWithHeader creates a block with the given header data. The
// header data is copied, changes to header and to the field values
// will not affect the block.
func NewBlockWithHeader(header *Header) *Block {
	return &Block{header: copyHeader(header)}
}

func copyHeader(h *Header) *Header {
	cpy := *h
	if cpy.Difficulty = new(big.Int); h.Difficulty != nil {
		cpy.Difficulty.Set(h.Difficulty)
	}
	if cpy.Number = new(big.Int); h.Number != nil {
		cpy.Number.Set(h.Number)
	}
	if cpy.GasLimit = new(big.Int); h.GasLimit != nil {
		cpy.GasLimit.Set(h.GasLimit)
	}
	if cpy.GasUsed = new(big.Int); h.GasUsed != nil {
		cpy.GasUsed.Set(h.GasUsed)
	}
	if len(h.Extra) > 0 {
		cpy.Extra = make([]byte, len(h.Extra))
		copy(cpy.Extra, h.Extra)
	}
	return &cpy
}

func (b *Block) ValidateFields() error {
	if b.header == nil {
		return fmt.Errorf("header is nil")
	}
	for i, transaction := range b.transactions {
		if transaction == nil {
			return fmt.Errorf("transaction %d is nil", i)
		}
	}
	for i, uncle := range b.uncles {
		if uncle == nil {
			return fmt.Errorf("uncle %d is nil", i)
		}
	}
	return nil
}

func (b *Block) DecodeRLP(s *rlp.Stream) error {
	var eb extblock
	_, size, _ := s.Kind()
	if err := s.Decode(&eb); err != nil {
		return err
	}
	b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs
	b.size.Store(common.StorageSize(rlp.ListSize(size)))
	return nil
}

func (b Block) EncodeRLP(w io.Writer) error {
	return rlp.Encode(w, extblock{
		Header: b.header,
		Txs:    b.transactions,
		Uncles: b.uncles,
	})
}

func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error {
	var sb storageblock
	if err := s.Decode(&sb); err != nil {
		return err
	}
	b.header, b.uncles, b.transactions, b.Td = sb.Header, sb.Uncles, sb.Txs, sb.TD
	return nil
}

func (b StorageBlock) EncodeRLP(w io.Writer) error {
	return rlp.Encode(w, storageblock{
		Header: b.header,
		Txs:    b.transactions,
		Uncles: b.uncles,
		TD:     b.Td,
	})
}

// TODO: copies
func (b *Block) Uncles() []*Header          { return b.uncles }
func (b *Block) Transactions() Transactions { return b.transactions }
func (b *Block) Receipts() Receipts         { return b.receipts }

func (b *Block) Transaction(hash common.Hash) *Transaction {
	for _, transaction := range b.transactions {
		if transaction.Hash() == hash {
			return transaction
		}
	}
	return nil
}

func (b *Block) Number() *big.Int     { return new(big.Int).Set(b.header.Number) }
func (b *Block) GasLimit() *big.Int   { return new(big.Int).Set(b.header.GasLimit) }
func (b *Block) GasUsed() *big.Int    { return new(big.Int).Set(b.header.GasUsed) }
func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) }

func (b *Block) NumberU64() uint64        { return b.header.Number.Uint64() }
func (b *Block) MixDigest() common.Hash   { return b.header.MixDigest }
func (b *Block) Nonce() uint64            { return binary.BigEndian.Uint64(b.header.Nonce[:]) }
func (b *Block) Bloom() Bloom             { return b.header.Bloom }
func (b *Block) Coinbase() common.Address { return b.header.Coinbase }
func (b *Block) Time() int64              { return int64(b.header.Time) }
func (b *Block) Root() common.Hash        { return b.header.Root }
func (b *Block) ParentHash() common.Hash  { return b.header.ParentHash }
func (b *Block) TxHash() common.Hash      { return b.header.TxHash }
func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash }
func (b *Block) UncleHash() common.Hash   { return b.header.UncleHash }
func (b *Block) Extra() []byte            { return common.CopyBytes(b.header.Extra) }

func (b *Block) Header() *Header { return copyHeader(b.header) }

func (b *Block) HashNoNonce() common.Hash {
	return b.header.HashNoNonce()
}

func (b *Block) Size() common.StorageSize {
	if size := b.size.Load(); size != nil {
		return size.(common.StorageSize)
	}
	c := writeCounter(0)
	rlp.Encode(&c, b)
	b.size.Store(common.StorageSize(c))
	return common.StorageSize(c)
}

type writeCounter common.StorageSize

func (c *writeCounter) Write(b []byte) (int, error) {
	*c += writeCounter(len(b))
	return len(b), nil
}

func CalcUncleHash(uncles []*Header) common.Hash {
	return rlpHash(uncles)
}

// WithMiningResult returns a new block with the data from b
// where nonce and mix digest are set to the provided values.
func (b *Block) WithMiningResult(nonce uint64, mixDigest common.Hash) *Block {
	cpy := *b.header
	binary.BigEndian.PutUint64(cpy.Nonce[:], nonce)
	cpy.MixDigest = mixDigest
	return &Block{
		header:       &cpy,
		transactions: b.transactions,
		receipts:     b.receipts,
		uncles:       b.uncles,
		Td:           b.Td,
	}
}

// Implement pow.Block

func (b *Block) Hash() common.Hash {
	if hash := b.hash.Load(); hash != nil {
		return hash.(common.Hash)
	}
	v := rlpHash(b.header)
	b.hash.Store(v)
	return v
}

func (b *Block) String() string {
	str := fmt.Sprintf(`Block(#%v): Size: %v TD: %v {
MinerHash: %x
%v
Transactions:
%v
Uncles:
%v
}
`, b.Number(), b.Size(), b.Td, b.header.HashNoNonce(), b.header, b.transactions, b.uncles)
	return str
}

func (h *Header) String() string {
	return fmt.Sprintf(`Header(%x):
[
	ParentHash:	    %x
	UncleHash:	    %x
	Coinbase:	    %x
	Root:		    %x
	TxSha		    %x
	ReceiptSha:	    %x
	Bloom:		    %x
	Difficulty:	    %v
	Number:		    %v
	GasLimit:	    %v
	GasUsed:	    %v
	Time:		    %v
	Extra:		    %s
	MixDigest:      %x
	Nonce:		    %x
]`, h.Hash(), h.ParentHash, h.UncleHash, h.Coinbase, h.Root, h.TxHash, h.ReceiptHash, h.Bloom, h.Difficulty, h.Number, h.GasLimit, h.GasUsed, h.Time, h.Extra, h.MixDigest, h.Nonce)
}

type Blocks []*Block

type BlockBy func(b1, b2 *Block) bool

func (self BlockBy) Sort(blocks Blocks) {
	bs := blockSorter{
		blocks: blocks,
		by:     self,
	}
	sort.Sort(bs)
}

type blockSorter struct {
	blocks Blocks
	by     func(b1, b2 *Block) bool
}

func (self blockSorter) Len() int { return len(self.blocks) }
func (self blockSorter) Swap(i, j int) {
	self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i]
}
func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) }

func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 }
<|endoftext|>


<commit_before>// +build !windows

package netfs

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"

	"github.com/jcelliott/lumber"

	"github.com/nanobox-io/nanobox/models"
	"github.com/nanobox-io/nanobox/provider"
	"github.com/nanobox-io/nanobox/util/data"
)

// EXPORTSFILE ...
const EXPORTSFILE = "/etc/exports"

// Exists checks to see if the mount already exists
func Exists(path string) bool {

	// generate the entry
	entry, err := entry(path)
	if err != nil {
		return false
	}

	// open the /etc/exports file for scanning...
	var f *os.File
	f, err = os.Open(EXPORTSFILE)
	if err != nil {
		return false
	}
	defer f.Close()

	// scan exports file looking for an entry for this path...
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		// scan each line to see if we have a match
		if scanner.Text() == entry {
			return true
		}
	}

	return false
}

// Add will export an nfs share
func Add(path string) error {

	// generate the entry
	entry, err := entry(path)
	if err != nil {
		return err
	}

	// add entry into the /etc/exports file
	if err := addEntry(entry); err != nil {
		return err
	}

	// reload nfsd
	if err := reloadServer(); err != nil {
		return err
	}

	return nil
}

// Remove will remove an nfs share
func Remove(path string) error {

	// generate the entry
	entry, err := entry(path)
	if err != nil {
		return err
	}

	if err := removeEntry(entry); err != nil {
		return err
	}

	// reload nfsd
	if err := reloadServer(); err != nil {
		return err
	}

	return nil
}

// Mount mounts a share on a guest machine
func Mount(hostPath, mountPath string) error {

	// ensure portmap is running
	cmd := []string{"/usr/local/sbin/portmap"}
	if b, err := provider.Run(cmd); err != nil {
		lumber.Debug("output: %s", b)
		return fmt.Errorf("portmap:%s", err.Error())
	}

	// ensure the destination directory exists
	cmd = []string{"/bin/mkdir", "-p", mountPath}
	if b, err := provider.Run(cmd); err != nil {
		lumber.Debug("output: %s", b)
		return fmt.Errorf("mkdir:%s", err.Error())
	}

	// TODO: this IP shouldn't be hardcoded, needs to be figured out!
	source := fmt.Sprintf("192.168.99.1:%s", hostPath)
	cmd = []string{"/bin/mount", "-t", "nfs", source, mountPath}
	if b, err := provider.Run(cmd); err != nil {
		lumber.Debug("output: %s", b)
		return fmt.Errorf("mount: output: %s err:%s", b, err.Error())
	}

	return nil
}

// entry generates the mount entry for the exports file
func entry(path string) (string, error) {

	// use the mountIP saved on the provider in the database
	provider := models.Provider{}
	if err := data.Get("global", "provider", &provider); err != nil {
		return "", err
	}
	if provider.MountIP == "" {
		return "", fmt.Errorf("there is no mount ip on the provider")
	}

	entry := fmt.Sprintf("\"%s\" %s -alldirs -mapall=%v:%v", path, provider.HostIP, uid(), gid())

	return entry, nil
}

// addEntry will add the entry into the /etc/exports file
func addEntry(entry string) error {

	// open exports file
	f, err := os.OpenFile(EXPORTSFILE, os.O_RDWR|os.O_APPEND, 0644)
	if err != nil {
		return err
	}
	defer f.Close()

	// write the entry to the file
	if _, err := f.WriteString(fmt.Sprintf("%s\n", entry)); err != nil {
		return err
	}

	return nil
}

// removeEntry will remove the entry from the /etc/exports file
func removeEntry(entry string) error {

	// contents will end up storing the entire contents of the file excluding the
	// entry that is trying to be removed
	var contents string

	// open exports file
	f, err := os.OpenFile(EXPORTSFILE, os.O_RDWR, 0644)
	if err != nil {
		return err
	}
	defer f.Close()

	// remove entry from /etc/exports
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {

		// if the line contain the entry skip it
		if scanner.Text() == entry {
			continue
		}

		// add each line back into the file
		contents += fmt.Sprintf("%s\n", scanner.Text())
	}

	// trim the contents to avoid any extra newlines
	contents = strings.TrimSpace(contents)

	// add a single newline for completeness
	contents += "\n"

	// write back the contents of the exports file minus the removed entry
	if err := ioutil.WriteFile(EXPORTSFILE, []byte(contents), 0644); err != nil {
		return err
	}

	return nil
}

// uid will grab the original uid that called sudo if set
func uid() (uid int) {

	//
	uid = os.Geteuid()

	// if this process was started with sudo, sudo is nice enough to set
	// environment variables to inform us about the user that executed sudo
	//
	// let's see if this is the case
	if sudoUID := os.Getenv("SUDO_UID"); sudoUID != "" {

		// SUDO_UID was set, so we need to cast the string to an int
		if s, err := strconv.Atoi(sudoUID); err == nil {
			uid = s
		}
	}

	return
}

// gid will grab the original gid that called sudo if set
func gid() (gid int) {

	//
	gid = os.Getgid()

	// if this process was started with sudo, sudo is nice enough to set
	// environment variables to inform us about the user that executed sudo
	//
	// let's see if this is the case
	if sudoGid := os.Getenv("SUDO_GID"); sudoGid != "" {

		// SUDO_UID was set, so we need to cast the string to an int
		if s, err := strconv.Atoi(sudoGid); err == nil {
			gid = s
		}
	}

	return
}
<commit_msg>put in a clean up process when we run the nanobox dev start for etc export this makes it so we can leave old apps in (for building purposes) but if the folder is removed we dont then fail to do a nfsd checkstatus<commit_after>// +build !windows

package netfs

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"

	"github.com/jcelliott/lumber"

	"github.com/nanobox-io/nanobox/models"
	"github.com/nanobox-io/nanobox/provider"
	"github.com/nanobox-io/nanobox/util/data"
)

// EXPORTSFILE ...
const EXPORTSFILE = "/etc/exports"

// Exists checks to see if the mount already exists
func Exists(path string) bool {

	// generate the entry
	entry, err := entry(path)
	if err != nil {
		return false
	}

	// open the /etc/exports file for scanning...
	var f *os.File
	f, err = os.Open(EXPORTSFILE)
	if err != nil {
		return false
	}
	defer f.Close()

	// scan exports file looking for an entry for this path...
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		// scan each line to see if we have a match
		if scanner.Text() == entry {
			return true
		}
	}

	return false
}

// Add will export an nfs share
func Add(path string) error {

	// generate the entry
	entry, err := entry(path)
	if err != nil {
		return err
	}

	// add entry into the /etc/exports file
	if err := addEntry(entry); err != nil {
		return err
	}

	if err := cleanExport(); err != nil {
		return err
	}

	// reload nfsd
	if err := reloadServer(); err != nil {
		return err
	}

	return nil
}

// Remove will remove an nfs share
func Remove(path string) error {

	// generate the entry
	entry, err := entry(path)
	if err != nil {
		return err
	}

	if err := removeEntry(entry); err != nil {
		return err
	}

	// reload nfsd
	if err := reloadServer(); err != nil {
		return err
	}

	return nil
}

// Mount mounts a share on a guest machine
func Mount(hostPath, mountPath string) error {

	// ensure portmap is running
	cmd := []string{"/usr/local/sbin/portmap"}
	if b, err := provider.Run(cmd); err != nil {
		lumber.Debug("output: %s", b)
		return fmt.Errorf("portmap:%s", err.Error())
	}

	// ensure the destination directory exists
	cmd = []string{"/bin/mkdir", "-p", mountPath}
	if b, err := provider.Run(cmd); err != nil {
		lumber.Debug("output: %s", b)
		return fmt.Errorf("mkdir:%s", err.Error())
	}

	// TODO: this IP shouldn't be hardcoded, needs to be figured out!
	source := fmt.Sprintf("192.168.99.1:%s", hostPath)
	cmd = []string{"/bin/mount", "-t", "nfs", source, mountPath}
	if b, err := provider.Run(cmd); err != nil {
		lumber.Debug("output: %s", b)
		return fmt.Errorf("mount: output: %s err:%s", b, err.Error())
	}

	return nil
}

// entry generates the mount entry for the exports file
func entry(path string) (string, error) {

	// use the mountIP saved on the provider in the database
	provider := models.Provider{}
	if err := data.Get("global", "provider", &provider); err != nil {
		return "", err
	}
	if provider.MountIP == "" {
		return "", fmt.Errorf("there is no mount ip on the provider")
	}

	entry := fmt.Sprintf("\"%s\" %s -alldirs -mapall=%v:%v", path, provider.HostIP, uid(), gid())

	return entry, nil
}

// addEntry will add the entry into the /etc/exports file
func addEntry(entry string) error {

	// open exports file
	f, err := os.OpenFile(EXPORTSFILE, os.O_RDWR|os.O_APPEND, 0644)
	if err != nil {
		return err
	}
	defer f.Close()

	// write the entry to the file
	if _, err := f.WriteString(fmt.Sprintf("%s\n", entry)); err != nil {
		return err
	}

	return nil
}

func cleanExport() error {

	// contents will end up storing the entire contents of the file excluding the
	// entry that no longer have a folder
	var contents string

	// open exports file
	f, err := os.OpenFile(EXPORTSFILE, os.O_RDWR, 0644)
	if err != nil {
		return err
	}
	defer f.Close()

	// remove entry from /etc/exports
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		parts := strings.Split(scanner.Text(), "\"")

		// if it starts with a "
		if len(parts) > 1 {
			fileInfo, err := os.Stat(parts[1])
			if err != nil || !fileInfo.IsDir() {
				continue
			}
		}

		// add each line back into the file
		contents += fmt.Sprintf("%s\n", scanner.Text())
	}

	// trim the contents to avoid any extra newlines
	contents = strings.TrimSpace(contents)

	// add a single newline for completeness
	contents += "\n"

	// write back the contents of the exports file minus the removed entry
	if err := ioutil.WriteFile(EXPORTSFILE, []byte(contents), 0644); err != nil {
		return err
	}

	return nil
}

// removeEntry will remove the entry from the /etc/exports file
func removeEntry(entry string) error {

	// contents will end up storing the entire contents of the file excluding the
	// entry that is trying to be removed
	var contents string

	// open exports file
	f, err := os.OpenFile(EXPORTSFILE, os.O_RDWR, 0644)
	if err != nil {
		return err
	}
	defer f.Close()

	// remove entry from /etc/exports
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {

		// if the line contain the entry skip it
		if scanner.Text() == entry {
			continue
		}

		// add each line back into the file
		contents += fmt.Sprintf("%s\n", scanner.Text())
	}

	// trim the contents to avoid any extra newlines
	contents = strings.TrimSpace(contents)

	// add a single newline for completeness
	contents += "\n"

	// write back the contents of the exports file minus the removed entry
	if err := ioutil.WriteFile(EXPORTSFILE, []byte(contents), 0644); err != nil {
		return err
	}

	return nil
}

// uid will grab the original uid that called sudo if set
func uid() (uid int) {

	//
	uid = os.Geteuid()

	// if this process was started with sudo, sudo is nice enough to set
	// environment variables to inform us about the user that executed sudo
	//
	// let's see if this is the case
	if sudoUID := os.Getenv("SUDO_UID"); sudoUID != "" {

		// SUDO_UID was set, so we need to cast the string to an int
		if s, err := strconv.Atoi(sudoUID); err == nil {
			uid = s
		}
	}

	return
}

// gid will grab the original gid that called sudo if set
func gid() (gid int) {

	//
	gid = os.Getgid()

	// if this process was started with sudo, sudo is nice enough to set
	// environment variables to inform us about the user that executed sudo
	//
	// let's see if this is the case
	if sudoGid := os.Getenv("SUDO_GID"); sudoGid != "" {

		// SUDO_UID was set, so we need to cast the string to an int
		if s, err := strconv.Atoi(sudoGid); err == nil {
			gid = s
		}
	}

	return
}
<|endoftext|>


<commit_before>// Copyright 2016 fatedier, fatedier@gmail.com
//
// 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 version

import (
	"strconv"
	"strings"
)

var version string = "0.23.0"

func Full() string {
	return version
}

func getSubVersion(v string, position int) int64 {
	arr := strings.Split(v, ".")
	if len(arr) < 3 {
		return 0
	}
	res, _ := strconv.ParseInt(arr[position], 10, 64)
	return res
}

func Proto(v string) int64 {
	return getSubVersion(v, 0)
}

func Major(v string) int64 {
	return getSubVersion(v, 1)
}

func Minor(v string) int64 {
	return getSubVersion(v, 2)
}

// add every case there if server will not accept client's protocol and return false
func Compat(client string) (ok bool, msg string) {
	if LessThan(client, "0.18.0") {
		return false, "Please upgrade your frpc version to at least 0.18.0"
	}
	return true, ""
}

func LessThan(client string, server string) bool {
	vc := Proto(client)
	vs := Proto(server)
	if vc > vs {
		return false
	} else if vc < vs {
		return true
	}

	vc = Major(client)
	vs = Major(server)
	if vc > vs {
		return false
	} else if vc < vs {
		return true
	}

	vc = Minor(client)
	vs = Minor(server)
	if vc > vs {
		return false
	} else if vc < vs {
		return true
	}
	return false
}
<commit_msg>bump version to v0.23.1<commit_after>// Copyright 2016 fatedier, fatedier@gmail.com
//
// 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 version

import (
	"strconv"
	"strings"
)

var version string = "0.23.1"

func Full() string {
	return version
}

func getSubVersion(v string, position int) int64 {
	arr := strings.Split(v, ".")
	if len(arr) < 3 {
		return 0
	}
	res, _ := strconv.ParseInt(arr[position], 10, 64)
	return res
}

func Proto(v string) int64 {
	return getSubVersion(v, 0)
}

func Major(v string) int64 {
	return getSubVersion(v, 1)
}

func Minor(v string) int64 {
	return getSubVersion(v, 2)
}

// add every case there if server will not accept client's protocol and return false
func Compat(client string) (ok bool, msg string) {
	if LessThan(client, "0.18.0") {
		return false, "Please upgrade your frpc version to at least 0.18.0"
	}
	return true, ""
}

func LessThan(client string, server string) bool {
	vc := Proto(client)
	vs := Proto(server)
	if vc > vs {
		return false
	} else if vc < vs {
		return true
	}

	vc = Major(client)
	vs = Major(server)
	if vc > vs {
		return false
	} else if vc < vs {
		return true
	}

	vc = Minor(client)
	vs = Minor(server)
	if vc > vs {
		return false
	} else if vc < vs {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package client

const (
	REGISTER_TYPE = "register"
)

type Register struct {
	Resource

	AccessKey string `json:"accessKey,omitempty" yaml:"access_key,omitempty"`

	K8sClientConfig K8sClientConfig `json:"k8sClientConfig,omitempty" yaml:"k8s_client_config,omitempty"`

	Key string `json:"key,omitempty" yaml:"key,omitempty"`

	SecretKey string `json:"secretKey,omitempty" yaml:"secret_key,omitempty"`

	State string `json:"state,omitempty" yaml:"state,omitempty"`

	Transitioning string `json:"transitioning,omitempty" yaml:"transitioning,omitempty"`

	TransitioningMessage string `json:"transitioningMessage,omitempty" yaml:"transitioning_message,omitempty"`
}

type RegisterCollection struct {
	Collection
	Data   []Register `json:"data,omitempty"`
	client *RegisterClient
}

type RegisterClient struct {
	rancherClient *RancherClient
}

type RegisterOperations interface {
	List(opts *ListOpts) (*RegisterCollection, error)
	Create(opts *Register) (*Register, error)
	Update(existing *Register, updates interface{}) (*Register, error)
	ById(id string) (*Register, error)
	Delete(container *Register) error

	ActionCreate(*Register) (*Register, error)

	ActionRemove(*Register) (*Register, error)
}

func newRegisterClient(rancherClient *RancherClient) *RegisterClient {
	return &RegisterClient{
		rancherClient: rancherClient,
	}
}

func (c *RegisterClient) Create(container *Register) (*Register, error) {
	resp := &Register{}
	err := c.rancherClient.doCreate(REGISTER_TYPE, container, resp)
	return resp, err
}

func (c *RegisterClient) Update(existing *Register, updates interface{}) (*Register, error) {
	resp := &Register{}
	err := c.rancherClient.doUpdate(REGISTER_TYPE, &existing.Resource, updates, resp)
	return resp, err
}

func (c *RegisterClient) List(opts *ListOpts) (*RegisterCollection, error) {
	resp := &RegisterCollection{}
	err := c.rancherClient.doList(REGISTER_TYPE, opts, resp)
	resp.client = c
	return resp, err
}

func (cc *RegisterCollection) Next() (*RegisterCollection, error) {
	if cc != nil && cc.Pagination != nil && cc.Pagination.Next != "" {
		resp := &RegisterCollection{}
		err := cc.client.rancherClient.doNext(cc.Pagination.Next, resp)
		resp.client = cc.client
		return resp, err
	}
	return nil, nil
}

func (c *RegisterClient) ById(id string) (*Register, error) {
	resp := &Register{}
	err := c.rancherClient.doById(REGISTER_TYPE, id, resp)
	if apiError, ok := err.(*ApiError); ok {
		if apiError.StatusCode == 404 {
			return nil, nil
		}
	}
	return resp, err
}

func (c *RegisterClient) Delete(container *Register) error {
	return c.rancherClient.doResourceDelete(REGISTER_TYPE, &container.Resource)
}

func (c *RegisterClient) ActionCreate(resource *Register) (*Register, error) {

	resp := &Register{}

	err := c.rancherClient.doAction(REGISTER_TYPE, "create", &resource.Resource, nil, resp)

	return resp, err
}

func (c *RegisterClient) ActionRemove(resource *Register) (*Register, error) {

	resp := &Register{}

	err := c.rancherClient.doAction(REGISTER_TYPE, "remove", &resource.Resource, nil, resp)

	return resp, err
}
<commit_msg>v3 Bump<commit_after>package client

const (
	REGISTER_TYPE = "register"
)

type Register struct {
	Resource

	AccessKey string `json:"accessKey,omitempty" yaml:"access_key,omitempty"`

	K8sClientConfig K8sClientConfig `json:"k8sClientConfig,omitempty" yaml:"k8s_client_config,omitempty"`

	Key string `json:"key,omitempty" yaml:"key,omitempty"`

	Orchestration string `json:"orchestration,omitempty" yaml:"orchestration,omitempty"`

	SecretKey string `json:"secretKey,omitempty" yaml:"secret_key,omitempty"`

	State string `json:"state,omitempty" yaml:"state,omitempty"`

	Transitioning string `json:"transitioning,omitempty" yaml:"transitioning,omitempty"`

	TransitioningMessage string `json:"transitioningMessage,omitempty" yaml:"transitioning_message,omitempty"`
}

type RegisterCollection struct {
	Collection
	Data   []Register `json:"data,omitempty"`
	client *RegisterClient
}

type RegisterClient struct {
	rancherClient *RancherClient
}

type RegisterOperations interface {
	List(opts *ListOpts) (*RegisterCollection, error)
	Create(opts *Register) (*Register, error)
	Update(existing *Register, updates interface{}) (*Register, error)
	ById(id string) (*Register, error)
	Delete(container *Register) error

	ActionCreate(*Register) (*Register, error)

	ActionRemove(*Register) (*Register, error)
}

func newRegisterClient(rancherClient *RancherClient) *RegisterClient {
	return &RegisterClient{
		rancherClient: rancherClient,
	}
}

func (c *RegisterClient) Create(container *Register) (*Register, error) {
	resp := &Register{}
	err := c.rancherClient.doCreate(REGISTER_TYPE, container, resp)
	return resp, err
}

func (c *RegisterClient) Update(existing *Register, updates interface{}) (*Register, error) {
	resp := &Register{}
	err := c.rancherClient.doUpdate(REGISTER_TYPE, &existing.Resource, updates, resp)
	return resp, err
}

func (c *RegisterClient) List(opts *ListOpts) (*RegisterCollection, error) {
	resp := &RegisterCollection{}
	err := c.rancherClient.doList(REGISTER_TYPE, opts, resp)
	resp.client = c
	return resp, err
}

func (cc *RegisterCollection) Next() (*RegisterCollection, error) {
	if cc != nil && cc.Pagination != nil && cc.Pagination.Next != "" {
		resp := &RegisterCollection{}
		err := cc.client.rancherClient.doNext(cc.Pagination.Next, resp)
		resp.client = cc.client
		return resp, err
	}
	return nil, nil
}

func (c *RegisterClient) ById(id string) (*Register, error) {
	resp := &Register{}
	err := c.rancherClient.doById(REGISTER_TYPE, id, resp)
	if apiError, ok := err.(*ApiError); ok {
		if apiError.StatusCode == 404 {
			return nil, nil
		}
	}
	return resp, err
}

func (c *RegisterClient) Delete(container *Register) error {
	return c.rancherClient.doResourceDelete(REGISTER_TYPE, &container.Resource)
}

func (c *RegisterClient) ActionCreate(resource *Register) (*Register, error) {

	resp := &Register{}

	err := c.rancherClient.doAction(REGISTER_TYPE, "create", &resource.Resource, nil, resp)

	return resp, err
}

func (c *RegisterClient) ActionRemove(resource *Register) (*Register, error) {

	resp := &Register{}

	err := c.rancherClient.doAction(REGISTER_TYPE, "remove", &resource.Resource, nil, resp)

	return resp, err
}
<|endoftext|>


<commit_before>// Copyright 2012 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package validation provide utilities functions for data validation.
package validation

import (
	"regexp"
)

// ValidateEmail checks whether a given email is valid.
func ValidateEmail(email string) bool {
	re := regexp.MustCompile(`^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$`)
	return re.MatchString(email)
}

// ValidateLength checks whether the given data match the given rules.
//
// It checks if the value has more or equal `min` chars and less or equal `max`
// chars. If you don't want to check both, just pass a zero value:
//
//     ValidateLength(value, 0, 100) // Checks if value has at most 100 characters
//     ValidateLength(value, 100, 0) // Checks if value has at least 100 characters
//     ValidateLength(value, 20, 100) // Checks if value has at least 20 characters and at most 100 characters
func ValidateLength(value string, min, max int) bool {
	l := len(value)
	if min > 0 && l < min {
		return false
	}
	if max > 0 && l > max {
		return false
	}
	return true
}
<commit_msg>validation: simplify docs<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package validation provide utilities functions for data validation.
package validation

import (
	"regexp"
)

func ValidateEmail(email string) bool {
	re := regexp.MustCompile(`^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$`)
	return re.MatchString(email)
}

// ValidateLength checks whether the given data match the given rules.
//
// It checks if the value has more or equal `min` chars and less or equal `max`
// chars. If you don't want to check both, just pass a zero value:
//
//     ValidateLength(value, 0, 100) // Checks if value has at most 100 characters
//     ValidateLength(value, 100, 0) // Checks if value has at least 100 characters
//     ValidateLength(value, 20, 100) // Checks if value has at least 20 characters and at most 100 characters
func ValidateLength(value string, min, max int) bool {
	l := len(value)
	if min > 0 && l < min {
		return false
	}
	if max > 0 && l > max {
		return false
	}
	return true
}
<|endoftext|>