<commit_before>package pr

import (
	"fmt"

	"github.com/abhinav/git-fu/editor"
	"github.com/abhinav/git-fu/gateway"
	"github.com/abhinav/git-fu/internal"

	"github.com/google/go-github/github"
)

// LandRequest is a request to land the given pull request.
type LandRequest struct {
	// PullRqeuest to land
	PullRequest *github.PullRequest

	// Name of the local branch that points to this PR or an empty string if a
	// local branch for this PR is not known.
	LocalBranch string
}

// Service TODO
type Service struct {
	GitHub gateway.GitHub
	Editor editor.Editor
	Git    gateway.Git
}

// Land the given pull request.
func (s *Service) Land(req *LandRequest) error {
	pr := req.PullRequest
	if err := UpdateMessage(s.Editor, pr); err != nil {
		return err
	}

	// If the base branch doesn't exist locally, check it out. If it exists,
	// it's okay for it to be out of sync with the remote.
	base := *pr.Base.Ref
	if !s.Git.DoesBranchExist(base) {
		if err := s.Git.CreateBranch(base, *pr.Base.Ref); err != nil {
			return err
		}
	}

	// If the branch is checked out locally, make sure it's in sync with
	// remote.
	if req.LocalBranch != "" {
		hash, err := s.Git.SHA1(req.LocalBranch)
		if err != nil {
			return err
		}

		if hash != *pr.Head.SHA {
			return fmt.Errorf(
				"SHA1 of local branch %v of pull request %v does not match GitHub. "+
					"Make sure that your local checkout of %v is in sync.",
				req.LocalBranch, *pr.HTMLURL, req.LocalBranch)
		}
	}

	if err := s.GitHub.SquashPullRequest(pr); err != nil {
		return err
	}

	if err := s.Git.Checkout(base); err != nil {
		return err
	}

	// TODO: Remove hard coded remote name
	if err := s.Git.Pull("origin", base); err != nil {
		return err
	}

	if req.LocalBranch != "" {
		if err := s.Git.DeleteBranch(req.LocalBranch); err != nil {
			return err
		}
	}

	// Nothing else to do if we don't own this pull request.
	if !s.GitHub.IsOwned(pr.Head) {
		return nil
	}

	dependents, err := s.GitHub.ListPullRequestsByBase(*pr.Head.Ref)
	if err != nil {
		return err
	}

	// No dependents. Delete the remote branch and the local tracking branch
	// for it.
	if len(dependents) == 0 {
		if err := s.GitHub.DeleteBranch(*pr.Head.Ref); err != nil {
			return err
		}

		if req.LocalBranch != "" {
			// TODO: Remove hard coded remote name
			if err := s.Git.DeleteRemoteTrackingBranch("origin", req.LocalBranch); err != nil {
				return err
			}
		}
	}

	return s.rebaseAll(base, dependents)
}

// Rebase all of the given pull requests onto the given base branch.
func (s *Service) rebaseAll(base string, prs []*github.PullRequest) error {
	var errors []error
	for _, pr := range prs {
		// We don't own this branch so we can't rebase it.
		if !s.GitHub.IsOwned(pr.Head) {
			// TODO record somewhere which PRs got skipped?
			continue
		}

		if err := s.rebaseOnto(base, pr); err != nil {
			errors = append(errors, err)
		}
	}

	return internal.MultiError(errors...)
}

// Rebase a specific PR and its dependents
func (s *Service) rebaseOnto(base string, pr *github.PullRequest) (err error) {
	patch, err := s.GitHub.GetPullRequestPatch(*pr.Number)
	if err != nil {
		return err
	}

	// Create a temporary branch off of the base to apply the patch onto.
	tempBranch := temporaryNameFor(s.Git, pr)
	// TODO: We need to create the temporary base branch only once for the
	// same merge base. This would make the operation faster for wider trees.

	fetch := gateway.FetchRequest{
		Remote:    "origin",
		RemoteRef: base,
		LocalRef:  tempBranch,
	}
	if err := s.Git.Fetch(&fetch); err != nil {
		return err
	}
	defer func() {
		err = internal.MultiError(err, s.Git.DeleteBranch(tempBranch))
	}()

	if err := s.Git.Checkout(tempBranch); err != nil {
		return err
	}
	defer func() {
		err = internal.MultiError(err, s.Git.Checkout(base))
	}()

	if err := s.Git.ApplyPatches(patch); err != nil {
		return err
	}

	// If we applied everything successfully, force push the change and update
	// the PR base.
	push := gateway.PushRequest{
		Remote:    "origin",
		LocalRef:  tempBranch,
		RemoteRef: *pr.Head.Ref,
		Force:     true,
	}
	if err := s.Git.Push(&push); err != nil {
		return err
	}

	if err := s.GitHub.SetPullRequestBase(*pr.Number, base); err != nil {
		return err
	}

	// If this PR had any dependents, rebase them onto its new head.
	dependents, err := s.GitHub.ListPullRequestsByBase(*pr.Head.Ref)
	if err != nil {
		return err
	}

	if len(dependents) > 0 {
		return s.rebaseAll(*pr.Head.Ref, dependents)
	}

	return nil
}

func temporaryNameFor(git gateway.Git, pr *github.PullRequest) string {
	base := fmt.Sprintf("pr-%v-rebase", *pr.Number)
	name := base
	for i := 1; git.DoesBranchExist(name); i++ {
		name = fmt.Sprintf("%v-%v", base, i)
	}
	return name
}
<commit_msg>pr/land: Delete remote branch after rebasing (#16)<commit_after>package pr

import (
	"fmt"

	"github.com/abhinav/git-fu/editor"
	"github.com/abhinav/git-fu/gateway"
	"github.com/abhinav/git-fu/internal"

	"github.com/google/go-github/github"
)

// LandRequest is a request to land the given pull request.
type LandRequest struct {
	// PullRqeuest to land
	PullRequest *github.PullRequest

	// Name of the local branch that points to this PR or an empty string if a
	// local branch for this PR is not known.
	LocalBranch string
}

// Service TODO
type Service struct {
	GitHub gateway.GitHub
	Editor editor.Editor
	Git    gateway.Git
}

// Land the given pull request.
func (s *Service) Land(req *LandRequest) error {
	pr := req.PullRequest
	if err := UpdateMessage(s.Editor, pr); err != nil {
		return err
	}

	// If the base branch doesn't exist locally, check it out. If it exists,
	// it's okay for it to be out of sync with the remote.
	base := *pr.Base.Ref
	if !s.Git.DoesBranchExist(base) {
		if err := s.Git.CreateBranch(base, *pr.Base.Ref); err != nil {
			return err
		}
	}

	// If the branch is checked out locally, make sure it's in sync with
	// remote.
	if req.LocalBranch != "" {
		hash, err := s.Git.SHA1(req.LocalBranch)
		if err != nil {
			return err
		}

		if hash != *pr.Head.SHA {
			return fmt.Errorf(
				"SHA1 of local branch %v of pull request %v does not match GitHub. "+
					"Make sure that your local checkout of %v is in sync.",
				req.LocalBranch, *pr.HTMLURL, req.LocalBranch)
		}
	}

	if err := s.GitHub.SquashPullRequest(pr); err != nil {
		return err
	}

	if err := s.Git.Checkout(base); err != nil {
		return err
	}

	// TODO: Remove hard coded remote name
	if err := s.Git.Pull("origin", base); err != nil {
		return err
	}

	if req.LocalBranch != "" {
		if err := s.Git.DeleteBranch(req.LocalBranch); err != nil {
			return err
		}
	}

	// Nothing else to do if we don't own this pull request.
	if !s.GitHub.IsOwned(pr.Head) {
		return nil
	}

	dependents, err := s.GitHub.ListPullRequestsByBase(*pr.Head.Ref)
	if err != nil {
		return err
	}

	if len(dependents) > 0 {
		if err := s.rebaseAll(base, dependents); err != nil {
			return fmt.Errorf("failed to rebase dependents of %v: %v", *pr.HTMLURL, err)
		}
	}

	// TODO: What happens on branch deletion if we had dependents but none
	// were owned by us?
	if err := s.GitHub.DeleteBranch(*pr.Head.Ref); err != nil {
		return err
	}

	if req.LocalBranch != "" {
		// TODO: Remove hard coded remote name
		if err := s.Git.DeleteRemoteTrackingBranch("origin", req.LocalBranch); err != nil {
			return err
		}
	}

	return nil
}

// Rebase all of the given pull requests onto the given base branch.
func (s *Service) rebaseAll(base string, prs []*github.PullRequest) error {
	var errors []error
	for _, pr := range prs {
		// We don't own this branch so we can't rebase it.
		if !s.GitHub.IsOwned(pr.Head) {
			// TODO record somewhere which PRs got skipped?
			continue
		}

		if err := s.rebaseOnto(base, pr); err != nil {
			errors = append(errors, err)
		}
	}

	return internal.MultiError(errors...)
}

// Rebase a specific PR and its dependents
func (s *Service) rebaseOnto(base string, pr *github.PullRequest) (err error) {
	patch, err := s.GitHub.GetPullRequestPatch(*pr.Number)
	if err != nil {
		return err
	}

	// Create a temporary branch off of the base to apply the patch onto.
	tempBranch := temporaryNameFor(s.Git, pr)
	// TODO: We need to create the temporary base branch only once for the
	// same merge base. This would make the operation faster for wider trees.

	fetch := gateway.FetchRequest{
		Remote:    "origin",
		RemoteRef: base,
		LocalRef:  tempBranch,
	}
	if err := s.Git.Fetch(&fetch); err != nil {
		return err
	}
	defer func() {
		err = internal.MultiError(err, s.Git.DeleteBranch(tempBranch))
	}()

	if err := s.Git.Checkout(tempBranch); err != nil {
		return err
	}
	defer func() {
		err = internal.MultiError(err, s.Git.Checkout(base))
	}()

	if err := s.Git.ApplyPatches(patch); err != nil {
		return err
	}

	// If we applied everything successfully, force push the change and update
	// the PR base.
	push := gateway.PushRequest{
		Remote:    "origin",
		LocalRef:  tempBranch,
		RemoteRef: *pr.Head.Ref,
		Force:     true,
	}
	if err := s.Git.Push(&push); err != nil {
		return err
	}

	if err := s.GitHub.SetPullRequestBase(*pr.Number, base); err != nil {
		return err
	}

	// TODO: If this worked out, we should probably reset the local branch for
	// this PR (if any) to the new head. Maybe by verifying a SHA before
	// rebasing.

	// If this PR had any dependents, rebase them onto its new head.
	dependents, err := s.GitHub.ListPullRequestsByBase(*pr.Head.Ref)
	if err != nil {
		return err
	}

	if len(dependents) > 0 {
		return s.rebaseAll(*pr.Head.Ref, dependents)
	}

	return nil
}

func temporaryNameFor(git gateway.Git, pr *github.PullRequest) string {
	base := fmt.Sprintf("pr-%v-rebase", *pr.Number)
	name := base
	for i := 1; git.DoesBranchExist(name); i++ {
		name = fmt.Sprintf("%v-%v", base, i)
	}
	return name
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package network

import (
	"context"
	"fmt"
	"net"
	"time"

	"github.com/ava-labs/avalanchego/utils"
)

// Dialer attempts to create a connection with the provided IP/port pair
type Dialer interface {
	// If [ctx] is canceled, gives up trying to connect to [ip]
	// and returns an error.
	Dial(ctx context.Context, ip utils.IPDesc) (net.Conn, error)
}

type dialer struct {
	network           string
	throttler         Throttler
	connectionTimeout time.Duration
}

type DialerConfig struct {
	throttleRps       uint32
	connectionTimeout time.Duration
}

func NewDialerConfig(throttleRps uint32, dialTimeout time.Duration) DialerConfig {
	return DialerConfig{
		throttleRps,
		dialTimeout,
	}
}

// NewDialer returns a new Dialer that calls `net.Dial` with the provided
// network.
func NewDialer(network string, dialerConfig DialerConfig) Dialer {
	var throttler Throttler
	if dialerConfig.throttleRps <= 0 {
		throttler = NewNoThrottler()
	} else {
		throttler = NewThrottler(int(dialerConfig.throttleRps))
	}

	return &dialer{
		network:           network,
		throttler:         throttler,
		connectionTimeout: dialerConfig.connectionTimeout,
	}
}

func (d *dialer) Dial(ctx context.Context, ip utils.IPDesc) (net.Conn, error) {
	if err := d.throttler.Acquire(ctx); err != nil {
		return nil, err
	}
	dialer := net.Dialer{Timeout: d.connectionTimeout}
	conn, err := dialer.DialContext(ctx, d.network, ip.String())
	if err != nil {
		return nil, fmt.Errorf("error while dialing %s: %s", ip, err)
	}
	return conn, nil
}
<commit_msg>AV-502 Limit number of outbound connections a node tries to open at once<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package network

import (
	"context"
	"fmt"
	"net"
	"time"

	"github.com/ava-labs/avalanchego/utils"
)

// Dialer attempts to create a connection with the provided IP/port pair
type Dialer interface {
	// If [ctx] is canceled, gives up trying to connect to [ip]
	// and returns an error.
	Dial(ctx context.Context, ip utils.IPDesc) (net.Conn, error)
}

type dialer struct {
	network           string
	throttler         Throttler
	connectionTimeout time.Duration
}

type DialerConfig struct {
	throttleRps       uint32
	connectionTimeout time.Duration
}

func NewDialerConfig(throttleRps uint32, dialTimeout time.Duration) DialerConfig {
	return DialerConfig{
		throttleRps,
		dialTimeout,
	}
}

// NewDialer returns a new Dialer that calls `net.Dial` with the provided
// network.
func NewDialer(network string, dialerConfig DialerConfig) Dialer {
	var throttler Throttler
	if dialerConfig.throttleRps <= 0 {
		fmt.Println("Throttling is turned off")
		throttler = NewNoThrottler()
	} else {
		fmt.Printf("Throttling %d rps\n", dialerConfig.throttleRps)
		throttler = NewThrottler(int(dialerConfig.throttleRps))
	}

	fmt.Println("Connection timeout", dialerConfig.connectionTimeout)

	return &dialer{
		network:           network,
		throttler:         throttler,
		connectionTimeout: dialerConfig.connectionTimeout,
	}
}

func (d *dialer) Dial(ctx context.Context, ip utils.IPDesc) (net.Conn, error) {
	if err := d.throttler.Acquire(ctx); err != nil {
		return nil, err
	}
	dialer := net.Dialer{Timeout: d.connectionTimeout}
	conn, err := dialer.DialContext(ctx, d.network, ip.String())
	if err != nil {
		return nil, fmt.Errorf("error while dialing %s: %s", ip, err)
	}
	return conn, nil
}
<|endoftext|>


<commit_before>// Package event handles incremental building of a log event.
package event

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"strconv"
	"time"
)

// Event holds a buffer of a log event content.
type Event struct {
	out      io.Writer
	buf      *bytes.Buffer
	wbuf     []byte
	maxLen   int
	exceeded int
	prefix   []byte
	suffix   []byte
	flush    chan chan bool
	start    chan (<-chan time.Time) // timer
	stop     chan bool
	close    chan bool
}

var autoFlushCalledHook = func() {}

// New creates an event buffer writing to the out writer on flush.
// When flush, the eol string is appended to the event content.
// When jsonKey is not empty, the output is wrapped into a JSON object
// with jsonKey as message key.
func New(out io.Writer, ctx map[string]string, maxLen int, eol string, jsonKey string) (e *Event, err error) {
	e = &Event{
		out:    out,
		buf:    bytes.NewBuffer(make([]byte, 0, 4096)),
		wbuf:   make([]byte, 0, 2),
		maxLen: maxLen,
		flush:  make(chan chan bool),
		start:  make(chan (<-chan time.Time)),
		stop:   make(chan bool),
		close:  make(chan bool, 1),
	}
	var ctxJSON []byte
	if len(ctx) > 0 {
		ctxJSON, err = json.Marshal(ctx)
		if err != nil {
			return nil, err
		}
		// Prepare for embedding by removing { } and append a comma
		ctxJSON = ctxJSON[1:]
		ctxJSON[len(ctxJSON)-1] = ','
	}
	if jsonKey != "" {
		e.prefix = []byte(fmt.Sprintf(`{%s"%s":"`, ctxJSON, jsonKey))
		e.suffix = []byte(fmt.Sprintf(`"}%s`, eol))
	} else {
		e.suffix = []byte(eol)
	}
	if maxLen > 0 && maxLen < len(e.prefix)+len(e.suffix) {
		return nil, errors.New("max len is lower than JSON envelope")
	}
	go e.flushLoop()
	return
}

// Empty returns true if the event's buffer is empty.
func (e *Event) Empty() bool {
	return e.buf.Len() == 0
}

// Write appends the contents of p to the buffer. The return value
// n is the length of p; err is always nil.
func (e *Event) Write(p []byte) (n int, err error) {
	if e.exceeded > 0 {
		e.exceeded += len(p)
		return
	}
	overhead := len(e.prefix) + len(e.suffix)
	if e.maxLen > 0 && e.buf.Len()+overhead > e.maxLen {
		e.exceeded = len(p)
		return
	}
	e.buf.Grow(len(p))
	for i, b := range p {
		e.wbuf = e.wbuf[:0]
		switch b {
		case '"':
			e.wbuf = append(e.wbuf, '\\', b)
		case '\\':
			e.wbuf = append(e.wbuf, `\\`...)
		case '\b':
			e.wbuf = append(e.wbuf, `\b`...)
		case '\f':
			e.wbuf = append(e.wbuf, `\f`...)
		case '\n':
			e.wbuf = append(e.wbuf, `\n`...)
		case '\r':
			e.wbuf = append(e.wbuf, `\r`...)
		case '\t':
			e.wbuf = append(e.wbuf, `\t`...)
		default:
			e.wbuf = append(e.wbuf, b)
		}
		if e.maxLen > 0 && e.buf.Len()+overhead+len(e.wbuf) > e.maxLen {
			e.exceeded = len(p) - i
			break
		}
		var _n int
		_n, err = e.buf.Write(e.wbuf)
		n += _n
		if err != nil {
			break
		}
	}
	return
}

// Flush appends the eol string to the buffer and copies it to the
// output writer. The buffer is reset after this operation so the
// event can be reused.
//
// If an AutoFlush was in progress, it is stopped by this operation.
func (e *Event) Flush() {
	if e.buf.Len() == 0 {
		return
	}
	e.Stop()
	c := make(chan bool)
	// Make the flushLoop to flush
	e.flush <- c
	// Wait for the flush to end
	<-c
}

func (e *Event) doFlush() {
	if e.buf.Len() == 0 {
		return
	}
	if len(e.prefix) > 0 {
		if _, err := e.out.Write(e.prefix); err != nil {
			log.Fatal(err)
		}
	}
	if e.exceeded > 0 {
		const elipse = "[]…" // size of … is 3 bytes
		eb := []byte(strconv.FormatInt(int64(e.exceeded+len(elipse)), 10))
		if t := e.buf.Len() - (len(eb) + len(elipse)); t > 0 {
			// Insert [total_bytes_truncated]… at the end of the message is possible
			e.buf.Truncate(t)
			e.buf.WriteByte(elipse[0])
			e.buf.Write(eb)
			e.buf.WriteString(elipse[1:])
		}
	}
	if len(e.suffix) > 0 {
		e.buf.Write(e.suffix)
	}
	if _, err := io.Copy(e.out, e.buf); err != nil {
		log.Fatal(err)
	}
	e.buf.Reset()
	e.exceeded = 0
}

// AutoFlush schedule a flush after delay.
func (e *Event) AutoFlush(delay time.Duration) {
	e.start <- time.After(delay)
}

// Stop clears the auto flush timer
func (e *Event) Stop() {
	e.stop <- true
}

// Close stops the flush loop and releases resources.
func (e *Event) Close() error {
	close(e.close)
	return nil
}

func (e *Event) flushLoop() {
	paused := make(<-chan time.Time) // will never fire
	next := paused
	for {
		select {
		case done := <-e.flush:
			e.doFlush()
			close(done) // notify caller
		case <-next:
			e.doFlush()
			autoFlushCalledHook()
		case <-e.stop:
			next = paused
		case timer := <-e.start:
			next = timer
		case <-e.close:
			return
		}
	}
}
<commit_msg>Sync writes with flushes<commit_after>// Package event handles incremental building of a log event.
package event

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"strconv"
	"time"
)

// Event holds a buffer of a log event content.
type Event struct {
	out      io.Writer
	buf      *bytes.Buffer
	wbuf     []byte
	maxLen   int
	exceeded int
	prefix   []byte
	suffix   []byte
	write    chan func()
	flush    chan chan bool
	start    chan (<-chan time.Time) // timer
	stop     chan bool
	close    chan bool
}

var autoFlushCalledHook = func() {}

// New creates an event buffer writing to the out writer on flush.
// When flush, the eol string is appended to the event content.
// When jsonKey is not empty, the output is wrapped into a JSON object
// with jsonKey as message key.
func New(out io.Writer, ctx map[string]string, maxLen int, eol string, jsonKey string) (e *Event, err error) {
	e = &Event{
		out:    out,
		buf:    bytes.NewBuffer(make([]byte, 0, 4096)),
		wbuf:   make([]byte, 0, 2),
		maxLen: maxLen,
		write:  make(chan func()),
		flush:  make(chan chan bool),
		start:  make(chan (<-chan time.Time)),
		stop:   make(chan bool),
		close:  make(chan bool, 1),
	}
	var ctxJSON []byte
	if len(ctx) > 0 {
		ctxJSON, err = json.Marshal(ctx)
		if err != nil {
			return nil, err
		}
		// Prepare for embedding by removing { } and append a comma
		ctxJSON = ctxJSON[1:]
		ctxJSON[len(ctxJSON)-1] = ','
	}
	if jsonKey != "" {
		e.prefix = []byte(fmt.Sprintf(`{%s"%s":"`, ctxJSON, jsonKey))
		e.suffix = []byte(fmt.Sprintf(`"}%s`, eol))
	} else {
		e.suffix = []byte(eol)
	}
	if maxLen > 0 && maxLen < len(e.prefix)+len(e.suffix) {
		return nil, errors.New("max len is lower than JSON envelope")
	}
	go e.writeLoop()
	return
}

// Empty returns true if the event's buffer is empty.
func (e *Event) Empty() bool {
	return e.buf.Len() == 0
}

// Write appends the contents of p to the buffer. The return value
// n is the length of p; err is always nil.
func (e *Event) Write(p []byte) (n int, err error) {
	done := make(chan struct{})
	e.write <- (func() {
		n, err = e.doWrite(p)
		close(done)
	})
	<-done
	return
}

func (e *Event) doWrite(p []byte) (n int, err error) {
	if e.exceeded > 0 {
		e.exceeded += len(p)
		return
	}
	overhead := len(e.prefix) + len(e.suffix)
	if e.maxLen > 0 && e.buf.Len()+overhead > e.maxLen {
		e.exceeded = len(p)
		return
	}
	e.buf.Grow(len(p))
	for i, b := range p {
		e.wbuf = e.wbuf[:0]
		switch b {
		case '"':
			e.wbuf = append(e.wbuf, '\\', b)
		case '\\':
			e.wbuf = append(e.wbuf, `\\`...)
		case '\b':
			e.wbuf = append(e.wbuf, `\b`...)
		case '\f':
			e.wbuf = append(e.wbuf, `\f`...)
		case '\n':
			e.wbuf = append(e.wbuf, `\n`...)
		case '\r':
			e.wbuf = append(e.wbuf, `\r`...)
		case '\t':
			e.wbuf = append(e.wbuf, `\t`...)
		default:
			e.wbuf = append(e.wbuf, b)
		}
		if e.maxLen > 0 && e.buf.Len()+overhead+len(e.wbuf) > e.maxLen {
			e.exceeded = len(p) - i
			break
		}
		var _n int
		_n, err = e.buf.Write(e.wbuf)
		n += _n
		if err != nil {
			break
		}
	}
	return
}

// Flush appends the eol string to the buffer and copies it to the
// output writer. The buffer is reset after this operation so the
// event can be reused.
//
// If an AutoFlush was in progress, it is stopped by this operation.
func (e *Event) Flush() {
	if e.buf.Len() == 0 {
		return
	}
	c := make(chan bool)
	// Make the flushLoop to flush
	e.flush <- c
	// Wait for the flush to end
	<-c
}

func (e *Event) doFlush() {
	if e.buf.Len() == 0 {
		return
	}
	if len(e.prefix) > 0 {
		if _, err := e.out.Write(e.prefix); err != nil {
			log.Fatal(err)
		}
	}
	if e.exceeded > 0 {
		const elipse = "[]…" // size of … is 3 bytes
		eb := []byte(strconv.FormatInt(int64(e.exceeded+len(elipse)), 10))
		if t := e.buf.Len() - (len(eb) + len(elipse)); t > 0 {
			// Insert [total_bytes_truncated]… at the end of the message is possible
			e.buf.Truncate(t)
			e.buf.WriteByte(elipse[0])
			e.buf.Write(eb)
			e.buf.WriteString(elipse[1:])
		}
	}
	if len(e.suffix) > 0 {
		e.buf.Write(e.suffix)
	}
	if _, err := io.Copy(e.out, e.buf); err != nil {
		log.Fatal(err)
	}
	e.buf.Reset()
	e.exceeded = 0
}

// AutoFlush schedule a flush after delay.
func (e *Event) AutoFlush(delay time.Duration) {
	e.start <- time.After(delay)
}

// Stop clears the auto flush timer
func (e *Event) Stop() {
	e.stop <- true
}

// Close stops the flush loop and releases resources.
func (e *Event) Close() error {
	close(e.close)
	return nil
}

func (e *Event) writeLoop() {
	paused := make(<-chan time.Time) // will never fire
	next := paused
	for {
		select {
		case cmd := <-e.write:
			cmd()
		case done := <-e.flush:
			e.doFlush()
			next = paused // cancel the autoflush
			close(done)   // notify caller
		case <-next:
			e.doFlush()
			next = paused
			autoFlushCalledHook()
		case <-e.stop:
			next = paused
		case timer := <-e.start:
			next = timer
		case <-e.close:
			return
		}
	}
}
<|endoftext|>


<commit_before>package jo

import "testing"

type eventMethodTest struct {
	ev Event
	is bool
}

var literals = []eventMethodTest{
	{Continue, false},
	{Done, false},
	{ObjectStart, false},
	{ObjectEnd, false},
	{KeyStart, false},
	{KeyEnd, false},
	{ArrayStart, false},
	{ArrayEnd, false},
	{SyntaxError, false},
	{StringStart, true},
	{StringEnd, true},
	{NumberStart, true},
	{NumberEnd, true},
	{BoolStart, true},
	{BoolEnd, true},
	{NullStart, true},
	{NullEnd, true},
}

var starts = []eventMethodTest{
	{Continue, false},
	{Done, false},
	{ObjectStart, true},
	{ObjectEnd, false},
	{KeyStart, true},
	{KeyEnd, false},
	{ArrayStart, true},
	{ArrayEnd, false},
	{SyntaxError, false},
	{StringStart, true},
	{StringEnd, false},
	{NumberStart, true},
	{NumberEnd, false},
	{BoolStart, true},
	{BoolEnd, false},
	{NullStart, true},
	{NullEnd, false},
}

var ends = []eventMethodTest{
	{Continue, false},
	{Done, false},
	{ObjectStart, false},
	{ObjectEnd, true},
	{KeyStart, false},
	{KeyEnd, true},
	{ArrayStart, false},
	{ArrayEnd, true},
	{SyntaxError, false},
	{StringStart, false},
	{StringEnd, true},
	{NumberStart, false},
	{NumberEnd, true},
	{BoolStart, false},
	{BoolEnd, true},
	{NullStart, false},
	{NullEnd, true},
}

func TestEventMethods(t *testing.T) {
	for _, test := range literals {
		if test.ev.IsLiteral() != test.is {
			t.Errorf("%s.IsLiteral() != %v", test.ev, test.is)
		}
	}

	for _, test := range starts {
		if test.ev.IsStart() != test.is {
			t.Errorf("%s.IsStart() != %v", test.ev, test.is)
		}
	}

	for _, test := range ends {
		if test.ev.IsEnd() != test.is {
			t.Errorf("%s.IsEnd() != %v", test.ev, test.is)
		}
	}
}
<commit_msg>Revert "Add tests for event methods"<commit_after><|endoftext|>


<commit_before>package fbot

import "testing"

func TestPanicIfEventNameIsInvalid(t *testing.T) {
	defer func() {
		if r := recover(); r == nil {
			t.Errorf("Given event name must be invalid")
		}
	}()

	On("invalid", func(_ Event) {})
}
<commit_msg>Test event triggering.<commit_after>package fbot

import "testing"

func TestPanicIfEventNameIsInvalid(t *testing.T) {
	defer func() {
		if r := recover(); r == nil {
			t.Error("Given event name must be invalid")
		}
	}()

	On("invalid", func(_ Event) {})
}

func TestOKEventTrigger(t *testing.T) {
	var ok bool

	On("message", func(_ Event) {
		ok = true
	})

	trigger("message", Event{})

	if !ok {
		t.Error("Event must be called")
	}
}
<|endoftext|>


<commit_before>package tagfast

import (
	"reflect"
	"strconv"
	"sync"
)

//[struct_name][field_name]
var CachedStructTags map[string]map[string]*TagFast = make(map[string]map[string]*TagFast)
var lock *sync.RWMutex = new(sync.RWMutex)

func CacheTag(struct_name string, field_name string, value *TagFast) {
	lock.Lock()
	defer lock.Unlock()
	if _, ok := CachedStructTags[struct_name]; !ok {
		CachedStructTags[struct_name] = make(map[string]*TagFast)
	}
	CachedStructTags[struct_name][field_name] = value
}

func GetTag(struct_name string, field_name string) (r *TagFast, ok bool) {
	lock.RLock()
	defer lock.RUnlock()
	var v map[string]*TagFast
	v, ok = CachedStructTags[struct_name]
	if !ok {
		return
	}
	r, ok = v[field_name]
	return
}

//usage: Tag1(t, i, "form")
func Tag1(t reflect.Type, field_no int, key string) (tag string) {
	f := t.Field(field_no)
	tag = Tag(t, f, key)
	return
}

//usage: Tag2(t, "Id", "form")
func Tag2(t reflect.Type, field_name string, key string) (tag string) {
	f, ok := t.FieldByName(field_name)
	if !ok {
		return ""
	}
	tag = Tag(t, f, key)
	return
}

func Tag(t reflect.Type, f reflect.StructField, key string) (tag string) {
	if f.Tag == "" {
		return ""
	}
	if v, ok := GetTag(t.String(), f.Name); ok {
		tag = v.Get(key)
	} else {
		v := TagFast{Tag: f.Tag}
		tag = v.Get(key)
		CacheTag(t.String(), f.Name, &v)
	}
	return
}

func Tago(t reflect.Type, f reflect.StructField, key string) (tag string, tf *TagFast) {
	if f.Tag == "" {
		return "", nil
	}
	if v, ok := GetTag(t.String(), f.Name); ok {
		tag = v.Get(key)
		tf = v
	} else {
		tf = &TagFast{Tag: f.Tag}
		tag = tf.Get(key)
		CacheTag(t.String(), f.Name, tf)
	}
	return
}

func ClearTag() {
	CachedStructTags = make(map[string]map[string]*TagFast)
}

func ParseStructTag(tag string) map[string]string {
	lock.Lock()
	defer lock.Unlock()
	var tagsArray map[string]string = make(map[string]string)
	for tag != "" {
		// skip leading space
		i := 0
		for i < len(tag) && tag[i] == ' ' {
			i++
		}
		tag = tag[i:]
		if tag == "" {
			break
		}

		// scan to colon.
		// a space or a quote is a syntax error
		i = 0
		for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' {
			i++
		}
		if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
			break
		}
		name := string(tag[:i])
		tag = tag[i+1:]

		// scan quoted string to find value
		i = 1
		for i < len(tag) && tag[i] != '"' {
			if tag[i] == '\\' {
				i++
			}
			i++
		}
		if i >= len(tag) {
			break
		}
		qvalue := string(tag[:i+1])
		tag = tag[i+1:]

		value, _ := strconv.Unquote(qvalue)
		tagsArray[name] = value
	}
	return tagsArray
}

type TagFast struct {
	Tag    reflect.StructTag
	Cached map[string]string
	Parsed map[string]interface{}
}

func (a *TagFast) Get(key string) string {
	if a.Cached == nil {
		a.Cached = ParseStructTag(string(a.Tag))
	}
	lock.RLock()
	defer lock.RUnlock()
	if v, ok := a.Cached[key]; ok {
		return v
	}
	return ""
}

func (a *TagFast) GetParsed(key string, fns ...func() interface{}) interface{} {
	if a.Parsed == nil {
		a.Parsed = make(map[string]interface{})
	}
	lock.Lock()
	defer lock.Unlock()
	if v, ok := a.Parsed[key]; ok {
		return v
	}
	if len(fns) > 0 {
		fn := fns[0]
		if fn != nil {
			v := fn()
			a.Parsed[key] = v
			return v
		}
	}
	return nil
}

func (a *TagFast) SetParsed(key string, value interface{}) bool {
	if a.Parsed == nil {
		a.Parsed = make(map[string]interface{})
	}
	lock.Lock()
	defer lock.Unlock()
	a.Parsed[key] = value
	return true
}
<commit_msg>update<commit_after>package tagfast

import (
	"reflect"
	"strconv"
	"sync"
)

//[struct_name][field_name]
var CachedStructTags map[string]map[string]*TagFast = make(map[string]map[string]*TagFast)
var lock *sync.RWMutex = new(sync.RWMutex)

func CacheTag(struct_name string, field_name string, value *TagFast) {
	lock.Lock()
	defer lock.Unlock()
	if _, ok := CachedStructTags[struct_name]; !ok {
		CachedStructTags[struct_name] = make(map[string]*TagFast)
	}
	CachedStructTags[struct_name][field_name] = value
}

func GetTag(struct_name string, field_name string) (r *TagFast, ok bool) {
	lock.RLock()
	defer lock.RUnlock()
	var v map[string]*TagFast
	v, ok = CachedStructTags[struct_name]
	if !ok {
		return
	}
	r, ok = v[field_name]
	return
}

//usage: Tag1(t, i, "form")
func Tag1(t reflect.Type, field_no int, key string) (tag string) {
	f := t.Field(field_no)
	tag = Tag(t, f, key)
	return
}

//usage: Tag2(t, "Id", "form")
func Tag2(t reflect.Type, field_name string, key string) (tag string) {
	f, ok := t.FieldByName(field_name)
	if !ok {
		return ""
	}
	tag = Tag(t, f, key)
	return
}

func Tag(t reflect.Type, f reflect.StructField, key string) (tag string) {
	if f.Tag == "" {
		return ""
	}
	if v, ok := GetTag(t.String(), f.Name); ok {
		tag = v.Get(key)
	} else {
		v := TagFast{Tag: f.Tag}
		tag = v.Get(key)
		CacheTag(t.String(), f.Name, &v)
	}
	return
}

func Tago(t reflect.Type, f reflect.StructField, key string) (tag string, tf *TagFast) {
	if f.Tag == "" {
		return "", nil
	}
	if v, ok := GetTag(t.String(), f.Name); ok {
		tag = v.Get(key)
		tf = v
	} else {
		tf = &TagFast{Tag: f.Tag}
		tag = tf.Get(key)
		CacheTag(t.String(), f.Name, tf)
	}
	return
}

func ClearTag() {
	CachedStructTags = make(map[string]map[string]*TagFast)
}

func ParseStructTag(tag string) map[string]string {
	lock.Lock()
	defer lock.Unlock()
	var tagsArray map[string]string = make(map[string]string)
	for tag != "" {
		// skip leading space
		i := 0
		for i < len(tag) && tag[i] == ' ' {
			i++
		}
		tag = tag[i:]
		if tag == "" {
			break
		}

		// scan to colon.
		// a space or a quote is a syntax error
		i = 0
		for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' {
			i++
		}
		if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
			break
		}
		name := string(tag[:i])
		tag = tag[i+1:]

		// scan quoted string to find value
		i = 1
		for i < len(tag) && tag[i] != '"' {
			if tag[i] == '\\' {
				i++
			}
			i++
		}
		if i >= len(tag) {
			break
		}
		qvalue := string(tag[:i+1])
		tag = tag[i+1:]

		value, _ := strconv.Unquote(qvalue)
		tagsArray[name] = value
	}
	return tagsArray
}

type TagFast struct {
	Tag    reflect.StructTag
	Cached map[string]string
	Parsed map[string]interface{}
}

func (a *TagFast) Get(key string) string {
	if a.Cached == nil {
		a.Cached = ParseStructTag(string(a.Tag))
	}
	lock.RLock()
	defer lock.RUnlock()
	if v, ok := a.Cached[key]; ok {
		return v
	}
	return ""
}

func (a *TagFast) GetParsed(key string, fns ...func() interface{}) interface{} {
	if a.Parsed == nil {
		a.Parsed = make(map[string]interface{})
	}
	lock.RLock()
	if v, ok := a.Parsed[key]; ok {
		lock.RUnlock()
		return v
	}
	lock.RUnlock()
	if len(fns) > 0 {
		fn := fns[0]
		if fn != nil {
			v := fn()
			a.SetParsed(key, v)
			return v
		}
	}
	return nil
}

func (a *TagFast) SetParsed(key string, value interface{}) bool {
	if a.Parsed == nil {
		a.Parsed = make(map[string]interface{})
	}
	lock.Lock()
	defer lock.Unlock()
	a.Parsed[key] = value
	return true
}
<|endoftext|>


<commit_before>package backoff

import (
	"math/rand"
	"time"
)

/*
ExponentialBackOff is an implementation of BackOff that increases the back off
period for each retry attempt using a randomization function that grows exponentially.

NextBackOff() is calculated using the following formula:

	randomized_interval =
	    retry_interval * (random value in range [1 - randomization_factor, 1 + randomization_factor])

In other words NextBackOff() will range between the randomization factor
percentage below and above the retry interval. For example, using 2 seconds as the base retry
interval and 0.5 as the randomization factor, the actual back off period used in the next retry
attempt will be between 1 and 3 seconds.

Note: max_interval caps the retry_interval and not the randomized_interval.

If the time elapsed since an ExponentialBackOff instance is created goes past the
max_elapsed_time then the method NextBackOff() starts returning backoff.Stop.
The elapsed time can be reset by calling Reset().

Example: The default retry_interval is .5 seconds, default randomization_factor is 0.5, default
multiplier is 1.5 and the default max_interval is 1 minute. For 10 tries the sequence will be
(values in seconds) and assuming we go over the max_elapsed_time on the 10th try:

	request#     retry_interval     randomized_interval

	1             0.5                [0.25,   0.75]
	2             0.75               [0.375,  1.125]
	3             1.125              [0.562,  1.687]
	4             1.687              [0.8435, 2.53]
	5             2.53               [1.265,  3.795]
	6             3.795              [1.897,  5.692]
	7             5.692              [2.846,  8.538]
	8             8.538              [4.269, 12.807]
	9            12.807              [6.403, 19.210]
	10           19.210              backoff.Stop

Implementation is not thread-safe.
*/
type ExponentialBackOff struct {
	InitialInterval     time.Duration
	RandomizationFactor float64
	Multiplier          float64
	MaxInterval         time.Duration
	MaxElapsedTime      time.Duration
	Clock               Clock

	currentInterval time.Duration
	startTime       time.Time
}

type Clock interface {
	Now() time.Time
}

// Default values for ExponentialBackOff.
const (
	DefaultInitialInterval     = 500 * time.Millisecond
	DefaultRandomizationFactor = 0.5
	DefaultMultiplier          = 1.5
	DefaultMaxInterval         = 60 * time.Second
	DefaultMaxElapsedTime      = 15 * time.Minute
)

// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
func NewExponentialBackOff() *ExponentialBackOff {
	return &ExponentialBackOff{
		InitialInterval:     DefaultInitialInterval,
		RandomizationFactor: DefaultRandomizationFactor,
		Multiplier:          DefaultMultiplier,
		MaxInterval:         DefaultMaxInterval,
		MaxElapsedTime:      DefaultMaxElapsedTime,
		Clock:               SystemClock,
	}
}

type systemClock struct{}

func (t systemClock) Now() time.Time {
	return time.Now()
}

var SystemClock = systemClock{}

// Reset the interval back to the initial retry interval and restarts the timer.
func (b *ExponentialBackOff) Reset() {
	b.currentInterval = b.InitialInterval
	b.startTime = b.Clock.Now()
}

// NextBackOff calculates the next back off interval using the formula:
// 	randomized_interval = retry_interval +/- (randomization_factor * retry_interval)
func (b *ExponentialBackOff) NextBackOff() time.Duration {
	// Make sure we have not gone over the maximum elapsed time.
	if b.GetElapsedTime() > b.MaxElapsedTime {
		return Stop
	}
	defer b.incrementCurrentInterval()
	return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
}

// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
// is created and is reset when Reset() is called.
//
// The elapsed time is computed using time.Now().UnixNano().
func (b *ExponentialBackOff) GetElapsedTime() time.Duration {
	return b.Clock.Now().Sub(b.startTime)
}

// Increments the current interval by multiplying it with the multiplier.
func (b *ExponentialBackOff) incrementCurrentInterval() {
	// Check for overflow, if overflow is detected set the current interval to the max interval.
	if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
		b.currentInterval = b.MaxInterval
	} else {
		b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
	}
}

// Returns a random value from the interval:
// 	[randomizationFactor * currentInterval, randomizationFactor * currentInterval].
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
	var delta = randomizationFactor * float64(currentInterval)
	var minInterval = float64(currentInterval) - delta
	var maxInterval = float64(currentInterval) + delta
	// Get a random value from the range [minInterval, maxInterval].
	// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
	// we want a 33% chance for selecting either 1, 2 or 3.
	return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
}
<commit_msg>add comments<commit_after>package backoff

import (
	"math/rand"
	"time"
)

/*
ExponentialBackOff is an implementation of BackOff that increases the back off
period for each retry attempt using a randomization function that grows exponentially.

NextBackOff() is calculated using the following formula:

	randomized_interval =
	    retry_interval * (random value in range [1 - randomization_factor, 1 + randomization_factor])

In other words NextBackOff() will range between the randomization factor
percentage below and above the retry interval. For example, using 2 seconds as the base retry
interval and 0.5 as the randomization factor, the actual back off period used in the next retry
attempt will be between 1 and 3 seconds.

Note: max_interval caps the retry_interval and not the randomized_interval.

If the time elapsed since an ExponentialBackOff instance is created goes past the
max_elapsed_time then the method NextBackOff() starts returning backoff.Stop.
The elapsed time can be reset by calling Reset().

Example: The default retry_interval is .5 seconds, default randomization_factor is 0.5, default
multiplier is 1.5 and the default max_interval is 1 minute. For 10 tries the sequence will be
(values in seconds) and assuming we go over the max_elapsed_time on the 10th try:

	request#     retry_interval     randomized_interval

	1             0.5                [0.25,   0.75]
	2             0.75               [0.375,  1.125]
	3             1.125              [0.562,  1.687]
	4             1.687              [0.8435, 2.53]
	5             2.53               [1.265,  3.795]
	6             3.795              [1.897,  5.692]
	7             5.692              [2.846,  8.538]
	8             8.538              [4.269, 12.807]
	9            12.807              [6.403, 19.210]
	10           19.210              backoff.Stop

Implementation is not thread-safe.
*/
type ExponentialBackOff struct {
	InitialInterval     time.Duration
	RandomizationFactor float64
	Multiplier          float64
	MaxInterval         time.Duration
	MaxElapsedTime      time.Duration
	Clock               Clock

	currentInterval time.Duration
	startTime       time.Time
}

// Clock is an interface that returns current time for BackOff.
type Clock interface {
	Now() time.Time
}

// Default values for ExponentialBackOff.
const (
	DefaultInitialInterval     = 500 * time.Millisecond
	DefaultRandomizationFactor = 0.5
	DefaultMultiplier          = 1.5
	DefaultMaxInterval         = 60 * time.Second
	DefaultMaxElapsedTime      = 15 * time.Minute
)

// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
func NewExponentialBackOff() *ExponentialBackOff {
	return &ExponentialBackOff{
		InitialInterval:     DefaultInitialInterval,
		RandomizationFactor: DefaultRandomizationFactor,
		Multiplier:          DefaultMultiplier,
		MaxInterval:         DefaultMaxInterval,
		MaxElapsedTime:      DefaultMaxElapsedTime,
		Clock:               SystemClock,
	}
}

type systemClock struct{}

func (t systemClock) Now() time.Time {
	return time.Now()
}

// SystemClock implements Clock interface that uses time.Now().
var SystemClock = systemClock{}

// Reset the interval back to the initial retry interval and restarts the timer.
func (b *ExponentialBackOff) Reset() {
	b.currentInterval = b.InitialInterval
	b.startTime = b.Clock.Now()
}

// NextBackOff calculates the next back off interval using the formula:
// 	randomized_interval = retry_interval +/- (randomization_factor * retry_interval)
func (b *ExponentialBackOff) NextBackOff() time.Duration {
	// Make sure we have not gone over the maximum elapsed time.
	if b.GetElapsedTime() > b.MaxElapsedTime {
		return Stop
	}
	defer b.incrementCurrentInterval()
	return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
}

// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
// is created and is reset when Reset() is called.
//
// The elapsed time is computed using time.Now().UnixNano().
func (b *ExponentialBackOff) GetElapsedTime() time.Duration {
	return b.Clock.Now().Sub(b.startTime)
}

// Increments the current interval by multiplying it with the multiplier.
func (b *ExponentialBackOff) incrementCurrentInterval() {
	// Check for overflow, if overflow is detected set the current interval to the max interval.
	if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
		b.currentInterval = b.MaxInterval
	} else {
		b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
	}
}

// Returns a random value from the interval:
// 	[randomizationFactor * currentInterval, randomizationFactor * currentInterval].
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
	var delta = randomizationFactor * float64(currentInterval)
	var minInterval = float64(currentInterval) - delta
	var maxInterval = float64(currentInterval) + delta
	// Get a random value from the range [minInterval, maxInterval].
	// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
	// we want a 33% chance for selecting either 1, 2 or 3.
	return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
}
<|endoftext|>


<commit_before>package piper

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"syscall"
	"time"
	"unsafe"

	"github.com/gorilla/websocket"
	"github.com/kr/pty"
)

type Pipe struct {
	conn *websocket.Conn
	send chan []byte
	opts Opts
}

type Opts struct {
	Tty    bool `json:"tty"`
	Width  int  `json:"width,omitempty"`
	Height int  `json:"height,omitempty"`
}

type pipedCmd struct {
	pipe   *Pipe
	cmd    *exec.Cmd
	stdin  io.WriteCloser
	stdout io.ReadCloser
	stderr io.ReadCloser
}

type Winsize struct {
	Height uint16
	Width  uint16
	x      uint16
	y      uint16
}

func NewClientPipe(host string, opts Opts) (*Pipe, error) {
	encoded, err := json.Marshal(opts)
	if err != nil {
		return nil, err
	}
	h := http.Header{}
	h.Add("X-Pipe-Opts", string(encoded))
	url := fmt.Sprintf("ws://%s", host)
	conn, _, err := websocket.DefaultDialer.Dial(url, h)
	if err != nil {
		return nil, err
	}
	pipe := &Pipe{
		conn: conn,
		send: make(chan []byte),
		opts: opts,
	}
	go pipe.writer()
	return pipe, nil
}

func NewServerPipe(req *http.Request, conn *websocket.Conn) (*Pipe, error) {
	xPipeOpts := req.Header.Get("X-Pipe-Opts")
	if xPipeOpts == "" {
		return nil, fmt.Errorf("missing X-Pipe-Opts")
	}
	var opts Opts
	if err := json.Unmarshal([]byte(xPipeOpts), &opts); err != nil {
		return nil, err
	}
	pipe := &Pipe{
		conn: conn,
		send: make(chan []byte),
		opts: opts,
	}
	go pipe.writer()
	return pipe, nil
}

func (pipe *Pipe) writer() {
	ticker := time.NewTicker(((60 * time.Second) * 9) / 10)
	defer func() {
		ticker.Stop()
		pipe.conn.Close()
	}()
	write := func(mt int, payload []byte) error {
		pipe.conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
		return pipe.conn.WriteMessage(mt, payload)
	}
	for {
		select {
		case msg, ok := <-pipe.send:
			if !ok {
				write(websocket.CloseMessage, []byte{})
				return
			}
			if err := write(websocket.BinaryMessage, msg); err != nil {
				return
			}
		case <-ticker.C:
			if err := write(websocket.PingMessage, []byte{}); err != nil {
				return
			}
		}
	}
}

func (pipe *Pipe) sendExit(code uint32) {
	msg := Message{
		Kind:     EXIT,
		ExitCode: code,
	}
	payload, _ := msg.Prepare()
	pipe.send <- payload
}

func (pipe *Pipe) sendEOF() {
	msg := Message{Kind: EOF}
	payload, _ := msg.Prepare()
	pipe.send <- payload
}

func (pipe *Pipe) RunCmd(cmd *exec.Cmd) error {
	if pipe.opts.Tty {
		return pipe.runPtyCmd(cmd)
	}
	return pipe.runStdCmd(cmd)
}

func (pipe *Pipe) runStdCmd(cmd *exec.Cmd) error {
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return err
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}
	pCmd := pipedCmd{
		pipe:   pipe,
		cmd:    cmd,
		stdin:  stdin,
		stdout: stdout,
		stderr: stderr,
	}
	return pCmd.run()
}

func (pipe *Pipe) runPtyCmd(cmd *exec.Cmd) error {
	py, tty, err := pty.Open()
	if err != nil {
		return err
	}
	defer tty.Close()
	env := os.Environ()
	env = append(env, "TERM=xterm")
	cmd.Env = env
	cmd.Stdout = tty
	cmd.Stdin = tty
	cmd.Stderr = tty
	cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
	ws := &Winsize{
		Width:  uint16(pipe.opts.Width),
		Height: uint16(pipe.opts.Height),
	}
	_, _, syserr := syscall.Syscall(syscall.SYS_IOCTL, py.Fd(), uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
	if syserr != 0 {
		return syserr
	}
	pCmd := pipedCmd{
		pipe:   pipe,
		cmd:    cmd,
		stdin:  py,
		stdout: py,
		stderr: nil,
	}
	return pCmd.run()
}

func (pCmd *pipedCmd) run() error {
	defer close(pCmd.pipe.send)
	go pCmd.pipe.copyFrom(pCmd.stdin, STDIN)
	go pCmd.pipe.copyTo(pCmd.stdout, STDOUT)
	if pCmd.stderr != nil {
		go pCmd.pipe.copyTo(pCmd.stderr, STDERR)
	}
	if err := pCmd.cmd.Start(); err != nil {
		pCmd.pipe.sendExit(uint32(1))
		return err
	}
	waitErrCh := make(chan error)
	go func() {
		waitErrCh <- pCmd.cmd.Wait()
	}()
	select {
	case err := <-waitErrCh:
		status, err := pCmd.exitStatus(err)
		if err != nil {
			pCmd.pipe.sendExit(uint32(1))
			return err
		}
		pCmd.pipe.sendExit(status)
	}
	return nil
}

func (pCmd *pipedCmd) exitStatus(err error) (uint32, error) {
	if err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				return uint32(status.ExitStatus()), nil
			}
		}
		return 0, err
	}
	return 0, nil
}

func (pipe *Pipe) copyFrom(w io.WriteCloser, kind int) error {
	defer w.Close()
	_, err := io.Copy(w, pipeIO{pipe: pipe, kind: kind})
	if err != nil {
		return err
	}
	return nil
}

func (pipe *Pipe) copyTo(r io.Reader, kind int) error {
	_, err := io.Copy(pipeIO{pipe: pipe, kind: kind}, r)
	if err != nil {
		return err
	}
	return nil
}

func (pipe *Pipe) Interact() (int, error) {
	go func() {
		stdinPipe := pipeIO{pipe: pipe, kind: STDIN}
		_, err := io.Copy(stdinPipe, os.Stdin)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		pipe.sendEOF()
	}()
	var exitCode int
loop:
	for {
		_, r, err := pipe.conn.NextReader()
		if err != nil {
			return 1, fmt.Errorf("reading message: %s", err)
		}
		m, err := DecodeMessage(r)
		if err != nil {
			return 1, fmt.Errorf("decoding message: %s", err)
		}
		switch m.Kind {
		case STDOUT:
			os.Stdout.Write(m.Payload)
			break
		case STDERR:
			os.Stderr.Write(m.Payload)
			break
		case EXIT:
			exitCode = int(m.ExitCode)
			break loop
		}
	}
	pipe.Close("")
	return exitCode, nil
}

func (pipe *Pipe) Close(msg string) {
	pipe.conn.WriteMessage(
		websocket.CloseMessage,
		websocket.FormatCloseMessage(websocket.CloseNormalClosure, msg),
	)
	pipe.conn.Close()
}
<commit_msg>fixed timeouts on read<commit_after>package piper

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"syscall"
	"time"
	"unsafe"

	"github.com/gorilla/websocket"
	"github.com/kr/pty"
)

const (
	// time allowed to write a message to the peer.
	writeWait = 10 * time.Second

	// time allowed to read the next pong message from the peer.
	pongWait = 60 * time.Second

	// send pings to peer with this period. must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10
)

type Pipe struct {
	conn *websocket.Conn
	send chan []byte
	opts Opts
}

type Opts struct {
	Tty    bool `json:"tty"`
	Width  int  `json:"width,omitempty"`
	Height int  `json:"height,omitempty"`
}

type pipedCmd struct {
	pipe   *Pipe
	cmd    *exec.Cmd
	stdin  io.WriteCloser
	stdout io.ReadCloser
	stderr io.ReadCloser
}

type Winsize struct {
	Height uint16
	Width  uint16
	x      uint16
	y      uint16
}

func NewClientPipe(host string, opts Opts) (*Pipe, error) {
	encoded, err := json.Marshal(opts)
	if err != nil {
		return nil, err
	}
	h := http.Header{}
	h.Add("X-Pipe-Opts", string(encoded))
	url := fmt.Sprintf("ws://%s", host)
	conn, _, err := websocket.DefaultDialer.Dial(url, h)
	if err != nil {
		return nil, err
	}
	pipe := &Pipe{
		conn: conn,
		send: make(chan []byte),
		opts: opts,
	}
	go pipe.writer()
	return pipe, nil
}

func NewServerPipe(req *http.Request, conn *websocket.Conn) (*Pipe, error) {
	xPipeOpts := req.Header.Get("X-Pipe-Opts")
	if xPipeOpts == "" {
		return nil, fmt.Errorf("missing X-Pipe-Opts")
	}
	var opts Opts
	if err := json.Unmarshal([]byte(xPipeOpts), &opts); err != nil {
		return nil, err
	}
	pipe := &Pipe{
		conn: conn,
		send: make(chan []byte),
		opts: opts,
	}
	go pipe.writer()
	return pipe, nil
}

func (pipe *Pipe) writer() {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		pipe.conn.Close()
	}()
	write := func(mt int, payload []byte) error {
		pipe.conn.SetWriteDeadline(time.Now().Add(writeWait))
		return pipe.conn.WriteMessage(mt, payload)
	}
	for {
		select {
		case msg, ok := <-pipe.send:
			if !ok {
				write(websocket.CloseMessage, []byte{})
				return
			}
			if err := write(websocket.BinaryMessage, msg); err != nil {
				return
			}
		case <-ticker.C:
			if err := write(websocket.PingMessage, []byte{}); err != nil {
				return
			}
		}
	}
}

func (pipe *Pipe) sendExit(code uint32) {
	msg := Message{
		Kind:     EXIT,
		ExitCode: code,
	}
	payload, _ := msg.Prepare()
	pipe.send <- payload
}

func (pipe *Pipe) sendEOF() {
	msg := Message{Kind: EOF}
	payload, _ := msg.Prepare()
	pipe.send <- payload
}

func (pipe *Pipe) RunCmd(cmd *exec.Cmd) error {
	if pipe.opts.Tty {
		return pipe.runPtyCmd(cmd)
	}
	return pipe.runStdCmd(cmd)
}

func (pipe *Pipe) runStdCmd(cmd *exec.Cmd) error {
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return err
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}
	pCmd := pipedCmd{
		pipe:   pipe,
		cmd:    cmd,
		stdin:  stdin,
		stdout: stdout,
		stderr: stderr,
	}
	return pCmd.run()
}

func (pipe *Pipe) runPtyCmd(cmd *exec.Cmd) error {
	py, tty, err := pty.Open()
	if err != nil {
		return err
	}
	defer tty.Close()
	env := os.Environ()
	env = append(env, "TERM=xterm")
	cmd.Env = env
	cmd.Stdout = tty
	cmd.Stdin = tty
	cmd.Stderr = tty
	cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
	ws := &Winsize{
		Width:  uint16(pipe.opts.Width),
		Height: uint16(pipe.opts.Height),
	}
	_, _, syserr := syscall.Syscall(syscall.SYS_IOCTL, py.Fd(), uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
	if syserr != 0 {
		return syserr
	}
	pCmd := pipedCmd{
		pipe:   pipe,
		cmd:    cmd,
		stdin:  py,
		stdout: py,
		stderr: nil,
	}
	return pCmd.run()
}

func (pCmd *pipedCmd) run() error {
	defer close(pCmd.pipe.send)
	go pCmd.pipe.copyFrom(pCmd.stdin, STDIN)
	go pCmd.pipe.copyTo(pCmd.stdout, STDOUT)
	if pCmd.stderr != nil {
		go pCmd.pipe.copyTo(pCmd.stderr, STDERR)
	}
	if err := pCmd.cmd.Start(); err != nil {
		pCmd.pipe.sendExit(uint32(1))
		return err
	}
	waitErrCh := make(chan error)
	go func() {
		waitErrCh <- pCmd.cmd.Wait()
	}()
	select {
	case err := <-waitErrCh:
		status, err := pCmd.exitStatus(err)
		if err != nil {
			pCmd.pipe.sendExit(uint32(1))
			return err
		}
		pCmd.pipe.sendExit(status)
	}
	return nil
}

func (pCmd *pipedCmd) exitStatus(err error) (uint32, error) {
	if err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				return uint32(status.ExitStatus()), nil
			}
		}
		return 0, err
	}
	return 0, nil
}

func (pipe *Pipe) copyFrom(w io.WriteCloser, kind int) error {
	defer w.Close()
	_, err := io.Copy(w, pipeIO{pipe: pipe, kind: kind})
	if err != nil {
		return err
	}
	return nil
}

func (pipe *Pipe) copyTo(r io.Reader, kind int) error {
	_, err := io.Copy(pipeIO{pipe: pipe, kind: kind}, r)
	if err != nil {
		return err
	}
	return nil
}

func (pipe *Pipe) Interact() (int, error) {
	go func() {
		stdinPipe := pipeIO{pipe: pipe, kind: STDIN}
		_, err := io.Copy(stdinPipe, os.Stdin)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		pipe.sendEOF()
	}()
	var exitCode int
	pongWait := 60 * time.Second
	pipe.conn.SetReadDeadline(time.Now().Add(pongWait))
	pipe.conn.SetPongHandler(func(string) error {
		pipe.conn.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})
loop:
	for {
		_, r, err := pipe.conn.NextReader()
		if err != nil {
			return 1, fmt.Errorf("reading message: %s", err)
		}
		m, err := DecodeMessage(r)
		if err != nil {
			return 1, fmt.Errorf("decoding message: %s", err)
		}
		switch m.Kind {
		case STDOUT:
			os.Stdout.Write(m.Payload)
			break
		case STDERR:
			os.Stderr.Write(m.Payload)
			break
		case EXIT:
			exitCode = int(m.ExitCode)
			break loop
		}
	}
	pipe.Close("")
	return exitCode, nil
}

func (pipe *Pipe) Close(msg string) {
	pipe.conn.WriteMessage(
		websocket.CloseMessage,
		websocket.FormatCloseMessage(websocket.CloseNormalClosure, msg),
	)
	pipe.conn.Close()
}
<|endoftext|>


<commit_before>/*

// also need TCPChan server

// similar API to TCPConn setup

func (c *TCPChan) Connect {
...
}

// communication with byte arrays which higher abstraction
// can then encode/decode from/onto

// HTTP wrapper?

*/

/*
  TODO: Also try implementing RPC nameserver (service discovery) proto
*/

package tcpchan

import (
  "net"
  _ "fmt"
)

type TCPChan struct {
  In  chan []byte
  Out chan []byte
  Err chan error
  Conn net.Conn
}

func (ch *TCPChan) Dial(addr string) error {

  ch.In = make(chan []byte, 100)
  ch.Out = make(chan []byte, 100)
  ch.Err = make(chan error, 100)
  conn, err := net.Dial("tcp", addr)
  if err != nil {
    return err
  }
  ch.Conn = conn
  go ch.checkOutgoing()
  go ch.checkIncoming()
  return nil
}

func (ch *TCPChan) checkOutgoing() {
  for {
    msg := <-ch.Out
    ch.Conn.Write(msg)
  }
}

func (ch *TCPChan) checkIncoming() {

  buffer := make([]byte, 1000)
  for {
    n, err := ch.Conn.Read(buffer)
    if err != nil {
      ch.Err <- err
    }
    ch.In <- buffer[:n]
  }
}
<commit_msg>clean up and remove unused code and comments<commit_after>// also need TCPChan server

package tcpchan

import (
  "net"
)

type TCPChan struct {
  In  chan []byte
  Out chan []byte
  Err chan error
  Conn net.Conn
}

func (ch *TCPChan) Dial(addr string) error {

  ch.In = make(chan []byte, 100)
  ch.Out = make(chan []byte, 100)
  ch.Err = make(chan error, 100)
  conn, err := net.Dial("tcp", addr)
  if err != nil {
    return err
  }
  ch.Conn = conn
  go ch.checkOutgoing()
  go ch.checkIncoming()
  return nil
}

func (ch *TCPChan) checkOutgoing() {
  for {
    msg := <-ch.Out
    ch.Conn.Write(msg)
  }
}

func (ch *TCPChan) checkIncoming() {

  buffer := make([]byte, 1000)
  for {
    n, err := ch.Conn.Read(buffer)
    if err != nil {
      ch.Err <- err
    }
    ch.In <- buffer[:n]
  }
}
<|endoftext|>


<commit_before>package nodos

import (
	"os"
	"path/filepath"
	"strings"

	"github.com/zetamatta/go-findfile"
)

func lookPath(dir1, patternBase string) (foundpath string) {
	pathExtList := filepath.SplitList(os.Getenv("PATHEXT"))
	ext := filepath.Ext(patternBase)
	var pattern string
	if ext == "" {
		pattern = patternBase + ".*"
	} else {
		pattern = patternBase
	}
	basename := filepath.Base(patternBase)
	names := map[string]int{strings.ToUpper(basename): 0}
	for i, ext1 := range pathExtList {
		names[strings.ToUpper(basename+ext1)] = i + 1
	}
	foundIndex := 999
	findfile.Walk(pattern, func(f *findfile.FileInfo) bool {
		if f.IsDir() {
			return true
		}
		if filepath.Ext(f.Name()) == "" {
			return true
		}
		if i, ok := names[strings.ToUpper(f.Name())]; ok && i < foundIndex {
			foundIndex = i
			foundpath = filepath.Join(dir1, f.Name())
			if f.IsReparsePoint() {
				var err error
				linkTo, err := os.Readlink(foundpath)
				if err == nil && linkTo != "" {
					foundpath = linkTo
					if !filepath.IsAbs(foundpath) {
						foundpath = filepath.Join(dir1, foundpath)
					}
				}
			}
		}
		return true
	})
	return
}

// LookCurdirT is the type for constant meaning the current directory should be looked.
type LookCurdirT int

const (
	// LookCurdirFirst means that the current directory should be looked at first.
	LookCurdirFirst LookCurdirT = iota
	// LookCurdirLast  means that the current directory should be looked at last.
	LookCurdirLast
	// LookCurdirNever menas that the current directory should be never looked.
	LookCurdirNever
)

// LookPath search `name` from %PATH% and the directories listed by
// the environment variables `envnames`.
func LookPath(where LookCurdirT, name string, envnames ...string) string {
	if strings.ContainsAny(name, "\\/:") {
		return lookPath(filepath.Dir(name), name)
	}
	var envlist strings.Builder
	if where == LookCurdirFirst {
		envlist.WriteRune('.')
		envlist.WriteRune(os.PathListSeparator)
	}
	envlist.WriteString(os.Getenv("PATH"))
	if where == LookCurdirLast {
		envlist.WriteRune(os.PathListSeparator)
		envlist.WriteRune('.')
	}
	for _, name1 := range envnames {
		envlist.WriteRune(os.PathListSeparator)
		envlist.WriteString(os.Getenv(name1))
	}
	// println(envlist.String())
	pathDirList := filepath.SplitList(envlist.String())

	for _, dir1 := range pathDirList {
		// println("lookPath:" + dir1)
		_dir1 := strings.TrimSpace(dir1)
		if _dir1 == "" {
			continue
		}
		if path := lookPath(dir1, filepath.Join(_dir1, name)); path != "" {
			// println("Found:" + path)
			return path
		}
	}
	return ""
}
<commit_msg>(#371) Could not execute `foo.bar.exe` as `foo.bar`<commit_after>package nodos

import (
	"os"
	"path/filepath"
	"strings"

	"github.com/zetamatta/go-findfile"
)

func lookPath(dir1, targetPath string) (foundpath string) {
	targetName := filepath.Base(targetPath)
	names := map[string]int{strings.ToUpper(targetName): 0}
	for i, ext1 := range filepath.SplitList(os.Getenv("PATHEXT")) {
		names[strings.ToUpper(targetName+ext1)] = i + 1
	}
	foundIndex := 999
	findfile.Walk(targetPath+"*", func(f *findfile.FileInfo) bool {
		if f.IsDir() || filepath.Ext(f.Name()) == "" {
			return true
		}
		if i, ok := names[strings.ToUpper(f.Name())]; ok && i < foundIndex {
			foundIndex = i
			foundpath = filepath.Join(dir1, f.Name())
			if f.IsReparsePoint() {
				linkTo, err := os.Readlink(foundpath)
				if err == nil && linkTo != "" {
					foundpath = linkTo
					if !filepath.IsAbs(foundpath) {
						foundpath = filepath.Join(dir1, foundpath)
					}
				}
			}
		}
		return true
	})
	return
}

// LookCurdirT is the type for constant meaning the current directory should be looked.
type LookCurdirT int

const (
	// LookCurdirFirst means that the current directory should be looked at first.
	LookCurdirFirst LookCurdirT = iota
	// LookCurdirLast  means that the current directory should be looked at last.
	LookCurdirLast
	// LookCurdirNever menas that the current directory should be never looked.
	LookCurdirNever
)

// LookPath search `name` from %PATH% and the directories listed by
// the environment variables `envnames`.
func LookPath(where LookCurdirT, name string, envnames ...string) string {
	if strings.ContainsAny(name, "\\/:") {
		return lookPath(filepath.Dir(name), name)
	}
	var envlist strings.Builder
	if where == LookCurdirFirst {
		envlist.WriteRune('.')
		envlist.WriteRune(os.PathListSeparator)
	}
	envlist.WriteString(os.Getenv("PATH"))
	if where == LookCurdirLast {
		envlist.WriteRune(os.PathListSeparator)
		envlist.WriteRune('.')
	}
	for _, name1 := range envnames {
		envlist.WriteRune(os.PathListSeparator)
		envlist.WriteString(os.Getenv(name1))
	}
	// println(envlist.String())
	pathDirList := filepath.SplitList(envlist.String())

	for _, dir1 := range pathDirList {
		// println("lookPath:" + dir1)
		_dir1 := strings.TrimSpace(dir1)
		if _dir1 == "" {
			continue
		}
		if path := lookPath(dir1, filepath.Join(_dir1, name)); path != "" {
			// println("Found:" + path)
			return path
		}
	}
	return ""
}
<|endoftext|>


<commit_before>package logfmt

import (
	"bytes"
	"io"
	"strconv"
	"unicode"
	"unicode/utf16"
	"unicode/utf8"
)

// Taken from Go's encoding/json and modified for use here.

// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var hex = "0123456789abcdef"

// NOTE: keep in sync with writeQuotedBytes below.
func writeQuotedString(w io.Writer, s string) (int, error) {
	buf := &bytes.Buffer{}
	buf.WriteByte('"')
	start := 0
	for i := 0; i < len(s); {
		if b := s[i]; b < utf8.RuneSelf {
			if 0x20 <= b && b != '\\' && b != '"' {
				i++
				continue
			}
			if start < i {
				buf.WriteString(s[start:i])
			}
			switch b {
			case '\\', '"':
				buf.WriteByte('\\')
				buf.WriteByte(b)
			case '\n':
				buf.WriteByte('\\')
				buf.WriteByte('n')
			case '\r':
				buf.WriteByte('\\')
				buf.WriteByte('r')
			case '\t':
				buf.WriteByte('\\')
				buf.WriteByte('t')
			default:
				// This encodes bytes < 0x20 except for \n, \r, and \t.
				buf.WriteString(`\u00`)
				buf.WriteByte(hex[b>>4])
				buf.WriteByte(hex[b&0xF])
			}
			i++
			start = i
			continue
		}
		c, size := utf8.DecodeRuneInString(s[i:])
		if c == utf8.RuneError && size == 1 {
			if start < i {
				buf.WriteString(s[start:i])
			}
			buf.WriteString(`\ufffd`)
			i += size
			start = i
			continue
		}
		i += size
	}
	if start < len(s) {
		buf.WriteString(s[start:])
	}
	buf.WriteByte('"')
	return w.Write(buf.Bytes())
}

// NOTE: keep in sync with writeQuoteString above.
func writeQuotedBytes(w io.Writer, s []byte) (int, error) {
	buf := &bytes.Buffer{}
	buf.WriteByte('"')
	start := 0
	for i := 0; i < len(s); {
		if b := s[i]; b < utf8.RuneSelf {
			if 0x20 <= b && b != '\\' && b != '"' {
				i++
				continue
			}
			if start < i {
				buf.Write(s[start:i])
			}
			switch b {
			case '\\', '"':
				buf.WriteByte('\\')
				buf.WriteByte(b)
			case '\n':
				buf.WriteByte('\\')
				buf.WriteByte('n')
			case '\r':
				buf.WriteByte('\\')
				buf.WriteByte('r')
			case '\t':
				buf.WriteByte('\\')
				buf.WriteByte('t')
			default:
				// This encodes bytes < 0x20 except for \n, \r, and \t.
				buf.WriteString(`\u00`)
				buf.WriteByte(hex[b>>4])
				buf.WriteByte(hex[b&0xF])
			}
			i++
			start = i
			continue
		}
		c, size := utf8.DecodeRune(s[i:])
		if c == utf8.RuneError && size == 1 {
			if start < i {
				buf.Write(s[start:i])
			}
			buf.WriteString(`\ufffd`)
			i += size
			start = i
			continue
		}
		i += size
	}
	if start < len(s) {
		buf.Write(s[start:])
	}
	buf.WriteByte('"')
	return w.Write(buf.Bytes())
}

// getu4 decodes \uXXXX from the beginning of s, returning the hex value,
// or it returns -1.
func getu4(s []byte) rune {
	if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
		return -1
	}
	r, err := strconv.ParseUint(string(s[2:6]), 16, 64)
	if err != nil {
		return -1
	}
	return rune(r)
}

func unquoteBytes(s []byte) (t []byte, ok bool) {
	if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
		return
	}
	s = s[1 : len(s)-1]

	// Check for unusual characters. If there are none,
	// then no unquoting is needed, so return a slice of the
	// original bytes.
	r := 0
	for r < len(s) {
		c := s[r]
		if c == '\\' || c == '"' || c < ' ' {
			break
		}
		if c < utf8.RuneSelf {
			r++
			continue
		}
		rr, size := utf8.DecodeRune(s[r:])
		if rr == utf8.RuneError && size == 1 {
			break
		}
		r += size
	}
	if r == len(s) {
		return s, true
	}

	b := make([]byte, len(s)+2*utf8.UTFMax)
	w := copy(b, s[0:r])
	for r < len(s) {
		// Out of room?  Can only happen if s is full of
		// malformed UTF-8 and we're replacing each
		// byte with RuneError.
		if w >= len(b)-2*utf8.UTFMax {
			nb := make([]byte, (len(b)+utf8.UTFMax)*2)
			copy(nb, b[0:w])
			b = nb
		}
		switch c := s[r]; {
		case c == '\\':
			r++
			if r >= len(s) {
				return
			}
			switch s[r] {
			default:
				return
			case '"', '\\', '/', '\'':
				b[w] = s[r]
				r++
				w++
			case 'b':
				b[w] = '\b'
				r++
				w++
			case 'f':
				b[w] = '\f'
				r++
				w++
			case 'n':
				b[w] = '\n'
				r++
				w++
			case 'r':
				b[w] = '\r'
				r++
				w++
			case 't':
				b[w] = '\t'
				r++
				w++
			case 'u':
				r--
				rr := getu4(s[r:])
				if rr < 0 {
					return
				}
				r += 6
				if utf16.IsSurrogate(rr) {
					rr1 := getu4(s[r:])
					if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar {
						// A valid pair; consume.
						r += 6
						w += utf8.EncodeRune(b[w:], dec)
						break
					}
					// Invalid surrogate; fall back to replacement rune.
					rr = unicode.ReplacementChar
				}
				w += utf8.EncodeRune(b[w:], rr)
			}

		// Quote, control characters are invalid.
		case c == '"', c < ' ':
			return

		// ASCII
		case c < utf8.RuneSelf:
			b[w] = c
			r++
			w++

		// Coerce to well-formed UTF-8.
		default:
			rr, size := utf8.DecodeRune(s[r:])
			r += size
			w += utf8.EncodeRune(b[w:], rr)
		}
	}
	return b[0:w], true
}
<commit_msg>Pool buffers for quoted strings and byte slices<commit_after>package logfmt

import (
	"bytes"
	"io"
	"strconv"
	"sync"
	"unicode"
	"unicode/utf16"
	"unicode/utf8"
)

// Taken from Go's encoding/json and modified for use here.

// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var hex = "0123456789abcdef"

var bufferPool = sync.Pool{
	New: func() interface{} {
		return &bytes.Buffer{}
	},
}

func getBuffer() *bytes.Buffer {
	return bufferPool.Get().(*bytes.Buffer)
}

func poolBuffer(buf *bytes.Buffer) {
	buf.Reset()
	bufferPool.Put(buf)
}

// NOTE: keep in sync with writeQuotedBytes below.
func writeQuotedString(w io.Writer, s string) (int, error) {
	buf := getBuffer()
	buf.WriteByte('"')
	start := 0
	for i := 0; i < len(s); {
		if b := s[i]; b < utf8.RuneSelf {
			if 0x20 <= b && b != '\\' && b != '"' {
				i++
				continue
			}
			if start < i {
				buf.WriteString(s[start:i])
			}
			switch b {
			case '\\', '"':
				buf.WriteByte('\\')
				buf.WriteByte(b)
			case '\n':
				buf.WriteByte('\\')
				buf.WriteByte('n')
			case '\r':
				buf.WriteByte('\\')
				buf.WriteByte('r')
			case '\t':
				buf.WriteByte('\\')
				buf.WriteByte('t')
			default:
				// This encodes bytes < 0x20 except for \n, \r, and \t.
				buf.WriteString(`\u00`)
				buf.WriteByte(hex[b>>4])
				buf.WriteByte(hex[b&0xF])
			}
			i++
			start = i
			continue
		}
		c, size := utf8.DecodeRuneInString(s[i:])
		if c == utf8.RuneError && size == 1 {
			if start < i {
				buf.WriteString(s[start:i])
			}
			buf.WriteString(`\ufffd`)
			i += size
			start = i
			continue
		}
		i += size
	}
	if start < len(s) {
		buf.WriteString(s[start:])
	}
	buf.WriteByte('"')
	n, err := w.Write(buf.Bytes())
	poolBuffer(buf)
	return n, err
}

// NOTE: keep in sync with writeQuoteString above.
func writeQuotedBytes(w io.Writer, s []byte) (int, error) {
	buf := getBuffer()
	buf.WriteByte('"')
	start := 0
	for i := 0; i < len(s); {
		if b := s[i]; b < utf8.RuneSelf {
			if 0x20 <= b && b != '\\' && b != '"' {
				i++
				continue
			}
			if start < i {
				buf.Write(s[start:i])
			}
			switch b {
			case '\\', '"':
				buf.WriteByte('\\')
				buf.WriteByte(b)
			case '\n':
				buf.WriteByte('\\')
				buf.WriteByte('n')
			case '\r':
				buf.WriteByte('\\')
				buf.WriteByte('r')
			case '\t':
				buf.WriteByte('\\')
				buf.WriteByte('t')
			default:
				// This encodes bytes < 0x20 except for \n, \r, and \t.
				buf.WriteString(`\u00`)
				buf.WriteByte(hex[b>>4])
				buf.WriteByte(hex[b&0xF])
			}
			i++
			start = i
			continue
		}
		c, size := utf8.DecodeRune(s[i:])
		if c == utf8.RuneError && size == 1 {
			if start < i {
				buf.Write(s[start:i])
			}
			buf.WriteString(`\ufffd`)
			i += size
			start = i
			continue
		}
		i += size
	}
	if start < len(s) {
		buf.Write(s[start:])
	}
	buf.WriteByte('"')
	n, err := w.Write(buf.Bytes())
	poolBuffer(buf)
	return n, err
}

// getu4 decodes \uXXXX from the beginning of s, returning the hex value,
// or it returns -1.
func getu4(s []byte) rune {
	if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
		return -1
	}
	r, err := strconv.ParseUint(string(s[2:6]), 16, 64)
	if err != nil {
		return -1
	}
	return rune(r)
}

func unquoteBytes(s []byte) (t []byte, ok bool) {
	if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
		return
	}
	s = s[1 : len(s)-1]

	// Check for unusual characters. If there are none,
	// then no unquoting is needed, so return a slice of the
	// original bytes.
	r := 0
	for r < len(s) {
		c := s[r]
		if c == '\\' || c == '"' || c < ' ' {
			break
		}
		if c < utf8.RuneSelf {
			r++
			continue
		}
		rr, size := utf8.DecodeRune(s[r:])
		if rr == utf8.RuneError && size == 1 {
			break
		}
		r += size
	}
	if r == len(s) {
		return s, true
	}

	b := make([]byte, len(s)+2*utf8.UTFMax)
	w := copy(b, s[0:r])
	for r < len(s) {
		// Out of room?  Can only happen if s is full of
		// malformed UTF-8 and we're replacing each
		// byte with RuneError.
		if w >= len(b)-2*utf8.UTFMax {
			nb := make([]byte, (len(b)+utf8.UTFMax)*2)
			copy(nb, b[0:w])
			b = nb
		}
		switch c := s[r]; {
		case c == '\\':
			r++
			if r >= len(s) {
				return
			}
			switch s[r] {
			default:
				return
			case '"', '\\', '/', '\'':
				b[w] = s[r]
				r++
				w++
			case 'b':
				b[w] = '\b'
				r++
				w++
			case 'f':
				b[w] = '\f'
				r++
				w++
			case 'n':
				b[w] = '\n'
				r++
				w++
			case 'r':
				b[w] = '\r'
				r++
				w++
			case 't':
				b[w] = '\t'
				r++
				w++
			case 'u':
				r--
				rr := getu4(s[r:])
				if rr < 0 {
					return
				}
				r += 6
				if utf16.IsSurrogate(rr) {
					rr1 := getu4(s[r:])
					if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar {
						// A valid pair; consume.
						r += 6
						w += utf8.EncodeRune(b[w:], dec)
						break
					}
					// Invalid surrogate; fall back to replacement rune.
					rr = unicode.ReplacementChar
				}
				w += utf8.EncodeRune(b[w:], rr)
			}

		// Quote, control characters are invalid.
		case c == '"', c < ' ':
			return

		// ASCII
		case c < utf8.RuneSelf:
			b[w] = c
			r++
			w++

		// Coerce to well-formed UTF-8.
		default:
			rr, size := utf8.DecodeRune(s[r:])
			r += size
			w += utf8.EncodeRune(b[w:], rr)
		}
	}
	return b[0:w], true
}
<|endoftext|>


<commit_before>package pages
import (
	"fmt"
	"image/draw"
	"image"
	"strconv"
	"net/http"
	"encoding/json"
	"b00lduck/datalogger/dataservice/orm"
	"io/ioutil"
)

type GasPage struct {
	BasePage
	imgBlk image.Image
	imgRed image.Image
	Counter int32
}

func CreateGasPage() Page {

	fmt.Println("CREATE GAS PAGE")

	arrowUp := LoadImage("arrow_up.gif")
	arrowDown := LoadImage("arrow_down.gif")

	gasPage := *new(GasPage)
	gasPage.BasePage = NewBasePage()

	gasPage.Counter = 22374090

	for i := 0; i < 8; i ++ {
		gasPage.BasePage.AddButton(arrowUp, 20 + i * 35, 60 , gasPage.incHandler(i))
		gasPage.BasePage.AddButton(arrowDown, 20 + i * 35, 150 , gasPage.decHandler(i))
	}

	gasPage.imgBlk = LoadImage("count_digits_grey.png")
	gasPage.imgRed = LoadImage("count_digits_red.png")

	return &gasPage

}

func (p *GasPage) incHandler(i int) func() {
	return func() {
		fmt.Printf("Digit %d >UP< pressed\n", i)
		p.changeCounter(i, true)
	}
}

func (p *GasPage) decHandler(i int) func() {
	return func() {
		fmt.Printf("Digit %d >DOWN< pressed\n", i)
		p.changeCounter(i, false)
	}
}

func (p GasPage) DrawDigit(target draw.Image, src image.Image, digit uint8, pos image.Point) {

	digitWidth := 25
	digitHeight := 40

	targetRect := image.Rect(pos.X, pos.Y, pos.X + digitWidth, pos.Y + digitHeight)

	sourcePos := image.Pt(digitWidth * int(digit), 0)

	draw.Draw(target, targetRect, src, sourcePos, draw.Over)

}

func (p *GasPage) Draw(target *draw.Image) {

	p.BaseDraw(target)

	cstr := fmt.Sprintf("%08d", p.Counter)

	for i := 0; i < 8; i++ {
		pint, _ := strconv.ParseUint(string(cstr[i]), 10, 8)
		if i < 5 {
			p.DrawDigit(*target, p.imgBlk, uint8(pint), image.Pt(20 + 36 * i, 100))
		} else {
			p.DrawDigit(*target, p.imgRed, uint8(pint), image.Pt(20 + 36 * i, 100))
		}

	}

}

func (p *GasPage) Process() bool {

	var contents []byte

	response, err := http.Get("http://localhost:8080/counter/1")
	if err != nil {
		fmt.Println(err)
		return false
	}

	defer response.Body.Close()
	contents, err = ioutil.ReadAll(response.Body)
	if err != nil {
		fmt.Println(err)
		return false
	}

	counter := orm.Counter{}
	err = json.Unmarshal(contents, &counter)
	if err != nil {
		fmt.Println(err)
		return false
	}

	p.Counter = int32(counter.Reading)

	return true
}

func (p *GasPage) changeCounter(digit int, direction bool) {

	cstr := fmt.Sprintf("%08d", p.Counter)

	pint, _ := strconv.ParseUint(string(cstr[digit]), 10, 8)

	factor := pow10(7 - digit)

	if direction {
		if pint == 9 {
			p.Counter -= factor * 9
		} else {
			p.Counter += factor
		}
	} else {
		if pint == 0 {
			p.Counter += factor * 9
		} else {
			p.Counter -= factor
		}
	}

	*(p.BasePage.DirtyChan) <- true

}

func pow10(n int) (ret int32) {
	ret = 1
	for ;n > 0;n-- {
		ret *= 10
	}
	return
}
<commit_msg>DISPLAY: abs correction<commit_after>package pages
import (
	"fmt"
	"image/draw"
	"image"
	"strconv"
	"net/http"
	"encoding/json"
	"b00lduck/datalogger/dataservice/orm"
	"io/ioutil"
	"strings"
)

type GasPage struct {
	BasePage
	imgBlk image.Image
	imgRed image.Image
	Counter int32
}

func CreateGasPage() Page {

	fmt.Println("CREATE GAS PAGE")

	arrowUp := LoadImage("arrow_up.gif")
	arrowDown := LoadImage("arrow_down.gif")

	gasPage := *new(GasPage)
	gasPage.BasePage = NewBasePage()

	gasPage.Counter = 0

	for i := 0; i < 8; i ++ {
		gasPage.BasePage.AddButton(arrowUp, 20 + i * 35, 60 , gasPage.incHandler(i))
		gasPage.BasePage.AddButton(arrowDown, 20 + i * 35, 150 , gasPage.decHandler(i))
	}

	gasPage.imgBlk = LoadImage("count_digits_grey.png")
	gasPage.imgRed = LoadImage("count_digits_red.png")

	return &gasPage

}

func (p *GasPage) incHandler(i int) func() {
	return func() {
		fmt.Printf("Digit %d >UP< pressed\n", i)
		p.changeCounter(i, true)
	}
}

func (p *GasPage) decHandler(i int) func() {
	return func() {
		fmt.Printf("Digit %d >DOWN< pressed\n", i)
		p.changeCounter(i, false)
	}
}

func (p GasPage) DrawDigit(target draw.Image, src image.Image, digit uint8, pos image.Point) {

	digitWidth := 25
	digitHeight := 40

	targetRect := image.Rect(pos.X, pos.Y, pos.X + digitWidth, pos.Y + digitHeight)

	sourcePos := image.Pt(digitWidth * int(digit), 0)

	draw.Draw(target, targetRect, src, sourcePos, draw.Over)

}

func (p *GasPage) Draw(target *draw.Image) {

	p.BaseDraw(target)

	cstr := fmt.Sprintf("%08d", p.Counter)

	for i := 0; i < 8; i++ {
		pint, _ := strconv.ParseUint(string(cstr[i]), 10, 8)
		if i < 5 {
			p.DrawDigit(*target, p.imgBlk, uint8(pint), image.Pt(20 + 36 * i, 100))
		} else {
			p.DrawDigit(*target, p.imgRed, uint8(pint), image.Pt(20 + 36 * i, 100))
		}

	}

}

func (p *GasPage) Process() bool {

	var contents []byte

	response, err := http.Get("http://localhost:8080/counter/1")
	if err != nil {
		fmt.Println(err)
		return false
	}

	defer response.Body.Close()
	contents, err = ioutil.ReadAll(response.Body)
	if err != nil {
		fmt.Println(err)
		return false
	}

	counter := orm.Counter{}
	err = json.Unmarshal(contents, &counter)
	if err != nil {
		fmt.Println(err)
		return false
	}

	p.Counter = int32(counter.Reading)

	return true
}

func (p *GasPage) changeCounter(digit int, direction bool) {

	cstr := fmt.Sprintf("%08d", p.Counter)

	pint, _ := strconv.ParseUint(string(cstr[digit]), 10, 8)

	factor := pow10(7 - digit)

	if direction {
		if pint == 9 {
			p.Counter -= factor * 9
		} else {
			p.Counter += factor
		}
	} else {
		if pint == 0 {
			p.Counter += factor * 9
		} else {
			p.Counter -= factor
		}
	}

	p.sendCounterValue()

	*(p.BasePage.DirtyChan) <- true

}

func (p *GasPage) sendCounterValue() {
	client := &http.Client{}
	sval := fmt.Sprintf("%d", p.Counter)
	fmt.Println(sval)
	request, err := http.NewRequest("PUT", "http://localhost:8080/counter/1/corr", strings.NewReader(sval))
	if err != nil {
		fmt.Println(err)
		return
	}
	request.ContentLength = int64(len(sval))
	_, err = client.Do(request)
	if err != nil {
		fmt.Println(err)
	}
}

func pow10(n int) (ret int32) {
	ret = 1
	for ;n > 0;n-- {
		ret *= 10
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2014, The Serviced Authors. All rights reserved.
// Use of this source code is governed by a
// license that can be found in the LICENSE file.

package facade

import (
	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/datastore"
	"github.com/zenoss/serviced/domain/host"
	"github.com/zenoss/serviced/domain/pool"

	"errors"
	"fmt"
	"time"
)

const (
	beforePoolUpdate = beforeEvent("BeforePoolUpdate")
	afterPoolUpdate  = afterEvent("AfterPoolUpdate")
	beforePoolAdd    = beforeEvent("BeforePoolAdd")
	afterPoolAdd     = afterEvent("AfterPoolAdd")
	beforePoolDelete = beforeEvent("BeforePoolDelete")
	afterPoolDelete  = afterEvent("AfterPoolDelete")
)

//PoolIPs type for IP resources available in a ResourcePool
type PoolIPs struct {
	PoolID  string
	HostIPs []host.HostIPResource
}

// GetPoolIPs gets all IPs available to a Pool
func (f *Facade) GetPoolIPs(ctx datastore.Context, poolID string) (*PoolIPs, error) {
	hosts, err := f.FindHostsInPool(ctx, poolID)
	if err != nil {
		return nil, err
	}
	hostIPs := make([]host.HostIPResource, 0)
	for _, h := range hosts {
		hostIPs = append(hostIPs, h.IPs...)
	}

	return &PoolIPs{PoolID: poolID, HostIPs: hostIPs}, nil
}

// AddResourcePool add resource pool to index
func (f *Facade) AddResourcePool(ctx datastore.Context, entity *pool.ResourcePool) error {
	glog.V(0).Infof("Facade.AddResourcePool: %+v", entity)
	exists, err := f.GetResourcePool(ctx, entity.ID)
	if err != nil {
		return err
	}
	if exists != nil {
		return fmt.Errorf("pool already exists: %s", entity.ID)
	}

	ec := newEventCtx()
	err = f.beforeEvent(beforePoolAdd, ec, entity)
	if err == nil {
		now := time.Now()
		entity.CreatedAt = now
		entity.UpdatedAt = now
		err = f.poolStore.Put(ctx, pool.Key(entity.ID), entity)
	}
	defer f.afterEvent(afterPoolAdd, ec, entity, err)
	return err
}

// GetResourcePool returns  an ResourcePool ip id. nil if not found
func (f *Facade) GetResourcePool(ctx datastore.Context, id string) (*pool.ResourcePool, error) {
	glog.V(2).Infof("Facade.GetResourcePool: id=%s", id)
	var entity pool.ResourcePool
	err := f.poolStore.Get(ctx, pool.Key(id), &entity)
	if datastore.IsErrNoSuchEntity(err) {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	return &entity, nil
}

// UpdateResourcePool updates a ResourcePool
func (f *Facade) UpdateResourcePool(ctx datastore.Context, entity *pool.ResourcePool) error {
	glog.V(2).Infof("Facade.UpdateResourcePool: %+v", entity)
	ec := newEventCtx()
	err := f.beforeEvent(beforePoolUpdate, ec, entity)
	if err == nil {
		now := time.Now()
		entity.UpdatedAt = now
		err = f.poolStore.Put(ctx, pool.Key(entity.ID), entity)
	}
	defer f.afterEvent(afterPoolUpdate, ec, entity, err)
	return err
}

// RemoveResourcePool removes a ResourcePool
func (f *Facade) RemoveResourcePool(ctx datastore.Context, id string) error {
	glog.V(2).Infof("Facade.RemoveResourcePool: %s", id)

	if hosts, err := f.FindHostsInPool(ctx, id); err != nil {
		return fmt.Errorf("error verifying no hosts in pool: %v", err)
	} else if len(hosts) > 0 {
		return errors.New("cannot delete resource pool with hosts")
	}

	return f.delete(ctx, f.poolStore, pool.Key(id), beforePoolDelete, afterPoolDelete)
}

//GetResourcePools Returns a list of all ResourcePools
func (f *Facade) GetResourcePools(ctx datastore.Context) ([]*pool.ResourcePool, error) {
	return f.poolStore.GetResourcePools(ctx)
}

//CreateDefaultPool creates the default pool if it does not exists, it is idempotent
func (f *Facade) CreateDefaultPool(ctx datastore.Context) error {
	entity, err := f.GetResourcePool(ctx, defaultPoolID)
	if err != nil {
		return fmt.Errorf("could not create default pool: %v", err)
	}
	if entity != nil {
		glog.V(4).Infof("'%s' resource pool already exists", defaultPoolID)
		return nil
	}

	glog.V(0).Infof("'%s' resource pool not found; creating...", defaultPoolID)
	entity = pool.New(defaultPoolID)
	return f.AddResourcePool(ctx, entity)
}

var defaultPoolID = "default"
<commit_msg>update log levels and remove defers<commit_after>// Copyright 2014, The Serviced Authors. All rights reserved.
// Use of this source code is governed by a
// license that can be found in the LICENSE file.

package facade

import (
	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/datastore"
	"github.com/zenoss/serviced/domain/host"
	"github.com/zenoss/serviced/domain/pool"

	"errors"
	"fmt"
	"time"
)

const (
	beforePoolUpdate = beforeEvent("BeforePoolUpdate")
	afterPoolUpdate  = afterEvent("AfterPoolUpdate")
	beforePoolAdd    = beforeEvent("BeforePoolAdd")
	afterPoolAdd     = afterEvent("AfterPoolAdd")
	beforePoolDelete = beforeEvent("BeforePoolDelete")
	afterPoolDelete  = afterEvent("AfterPoolDelete")
)

//PoolIPs type for IP resources available in a ResourcePool
type PoolIPs struct {
	PoolID  string
	HostIPs []host.HostIPResource
}

// GetPoolIPs gets all IPs available to a Pool
func (f *Facade) GetPoolIPs(ctx datastore.Context, poolID string) (*PoolIPs, error) {
	hosts, err := f.FindHostsInPool(ctx, poolID)
	if err != nil {
		return nil, err
	}
	hostIPs := make([]host.HostIPResource, 0)
	for _, h := range hosts {
		hostIPs = append(hostIPs, h.IPs...)
	}

	return &PoolIPs{PoolID: poolID, HostIPs: hostIPs}, nil
}

// AddResourcePool add resource pool to index
func (f *Facade) AddResourcePool(ctx datastore.Context, entity *pool.ResourcePool) error {
	glog.V(2).Infof("Facade.AddResourcePool: %+v", entity)
	exists, err := f.GetResourcePool(ctx, entity.ID)
	if err != nil {
		return err
	}
	if exists != nil {
		return fmt.Errorf("pool already exists: %s", entity.ID)
	}

	ec := newEventCtx()
	err = f.beforeEvent(beforePoolAdd, ec, entity)
	if err == nil {
		now := time.Now()
		entity.CreatedAt = now
		entity.UpdatedAt = now
		err = f.poolStore.Put(ctx, pool.Key(entity.ID), entity)
	}
	f.afterEvent(afterPoolAdd, ec, entity, err)
	return err
}

// GetResourcePool returns  an ResourcePool ip id. nil if not found
func (f *Facade) GetResourcePool(ctx datastore.Context, id string) (*pool.ResourcePool, error) {
	glog.V(2).Infof("Facade.GetResourcePool: id=%s", id)
	var entity pool.ResourcePool
	err := f.poolStore.Get(ctx, pool.Key(id), &entity)
	if datastore.IsErrNoSuchEntity(err) {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	return &entity, nil
}

// UpdateResourcePool updates a ResourcePool
func (f *Facade) UpdateResourcePool(ctx datastore.Context, entity *pool.ResourcePool) error {
	glog.V(2).Infof("Facade.UpdateResourcePool: %+v", entity)
	ec := newEventCtx()
	err := f.beforeEvent(beforePoolUpdate, ec, entity)
	if err == nil {
		now := time.Now()
		entity.UpdatedAt = now
		err = f.poolStore.Put(ctx, pool.Key(entity.ID), entity)
	}
	f.afterEvent(afterPoolUpdate, ec, entity, err)
	return err
}

// RemoveResourcePool removes a ResourcePool
func (f *Facade) RemoveResourcePool(ctx datastore.Context, id string) error {
	glog.V(2).Infof("Facade.RemoveResourcePool: %s", id)

	if hosts, err := f.FindHostsInPool(ctx, id); err != nil {
		return fmt.Errorf("error verifying no hosts in pool: %v", err)
	} else if len(hosts) > 0 {
		return errors.New("cannot delete resource pool with hosts")
	}

	return f.delete(ctx, f.poolStore, pool.Key(id), beforePoolDelete, afterPoolDelete)
}

//GetResourcePools Returns a list of all ResourcePools
func (f *Facade) GetResourcePools(ctx datastore.Context) ([]*pool.ResourcePool, error) {
	return f.poolStore.GetResourcePools(ctx)
}

//CreateDefaultPool creates the default pool if it does not exists, it is idempotent
func (f *Facade) CreateDefaultPool(ctx datastore.Context) error {
	entity, err := f.GetResourcePool(ctx, defaultPoolID)
	if err != nil {
		return fmt.Errorf("could not create default pool: %v", err)
	}
	if entity != nil {
		glog.V(4).Infof("'%s' resource pool already exists", defaultPoolID)
		return nil
	}

	glog.Infof("'%s' resource pool not found; creating...", defaultPoolID)
	entity = pool.New(defaultPoolID)
	return f.AddResourcePool(ctx, entity)
}

var defaultPoolID = "default"
<|endoftext|>