<commit_before>package pg

import (
	"container/list"
	"sync"
	"time"

	"github.com/golang/glog"
)

type connPool struct {
	New func() (*conn, error)

	cond  *sync.Cond
	conns *list.List

	idleNum     int
	maxSize     int
	idleTimeout time.Duration

	closed bool
}

func newConnPool(
	dial func() (*conn, error),
	maxSize int, idleTimeout time.Duration,
) *connPool {
	return &connPool{
		New: dial,

		cond:  sync.NewCond(&sync.Mutex{}),
		conns: list.New(),

		maxSize:     maxSize,
		idleTimeout: idleTimeout,
	}
}

func (p *connPool) Get() (*conn, bool, error) {
	p.cond.L.Lock()

	if p.closed {
		p.cond.L.Unlock()
		return nil, false, errClosed
	}

	if p.idleTimeout > 0 {
		for el := p.conns.Front(); el != nil; el = el.Next() {
			cn := el.Value.(*conn)
			if cn.inUse {
				break
			}
			if time.Since(cn.usedAt) > p.idleTimeout {
				if err := p.remove(cn); err != nil {
					glog.Errorf("Remove failed: %s", err)
				}
			}
		}
	}

	for p.conns.Len() >= p.maxSize && p.idleNum == 0 {
		p.cond.Wait()
	}

	if p.idleNum > 0 {
		elem := p.conns.Front()
		cn := elem.Value.(*conn)
		if cn.inUse {
			panic("pool: precondition failed")
		}
		cn.inUse = true
		p.conns.MoveToBack(elem)
		p.idleNum--

		p.cond.L.Unlock()
		return cn, false, nil
	}

	if p.conns.Len() < p.maxSize {
		cn, err := p.New()
		if err != nil {
			p.cond.L.Unlock()
			return nil, false, err
		}

		cn.inUse = true
		cn.elem = p.conns.PushBack(cn)

		p.cond.L.Unlock()
		return cn, true, nil
	}

	panic("not reached")
}

func (p *connPool) Put(cn *conn) error {
	if cn.br.Buffered() > 0 {
		panic("connection has unread data")
	}

	cn.buf.Reset()
	if p.idleTimeout > 0 {
		cn.usedAt = time.Now()
	}

	p.cond.L.Lock()
	if p.closed {
		p.cond.L.Unlock()
		return errClosed
	}
	cn.inUse = false
	p.conns.MoveToFront(cn.elem)
	p.idleNum++
	p.cond.Signal()
	p.cond.L.Unlock()
	return nil
}

func (p *connPool) remove(cn *conn) (err error) {
	if cn != nil {
		err = cn.Close()
	}
	p.conns.Remove(cn.elem)
	cn.elem = nil
	return err
}

func (p *connPool) Remove(cn *conn) error {
	p.cond.L.Lock()
	if p.closed {
		// Noop, connection is already closed.
		p.cond.L.Unlock()
		return nil
	}
	err := p.remove(cn)
	p.cond.Signal()
	p.cond.L.Unlock()
	return err
}

// Returns number of idle connections.
func (p *connPool) Len() int {
	defer p.cond.L.Unlock()
	p.cond.L.Lock()
	return p.idleNum
}

// Returns number of connections in the pool.
func (p *connPool) Size() int {
	defer p.cond.L.Unlock()
	p.cond.L.Lock()
	return p.conns.Len()
}

func (p *connPool) Close() error {
	defer p.cond.L.Unlock()
	p.cond.L.Lock()

	if p.closed {
		return errClosed
	}
	p.closed = true
	var retErr error
	for e := p.conns.Front(); e != nil; e = e.Next() {
		cn := e.Value.(*conn)
		if err := cn.Close(); err != nil {
			glog.Errorf("cn.Close failed: %s", err)
			retErr = err
		}
		cn.elem = nil
	}
	p.conns = nil
	return retErr
}
<commit_msg>Replace panic with log message.<commit_after>package pg

import (
	"container/list"
	"sync"
	"time"

	"github.com/golang/glog"
)

type connPool struct {
	New func() (*conn, error)

	cond  *sync.Cond
	conns *list.List

	idleNum     int
	maxSize     int
	idleTimeout time.Duration

	closed bool
}

func newConnPool(
	dial func() (*conn, error),
	maxSize int, idleTimeout time.Duration,
) *connPool {
	return &connPool{
		New: dial,

		cond:  sync.NewCond(&sync.Mutex{}),
		conns: list.New(),

		maxSize:     maxSize,
		idleTimeout: idleTimeout,
	}
}

func (p *connPool) Get() (*conn, bool, error) {
	p.cond.L.Lock()

	if p.closed {
		p.cond.L.Unlock()
		return nil, false, errClosed
	}

	if p.idleTimeout > 0 {
		for el := p.conns.Front(); el != nil; el = el.Next() {
			cn := el.Value.(*conn)
			if cn.inUse {
				break
			}
			if time.Since(cn.usedAt) > p.idleTimeout {
				if err := p.remove(cn); err != nil {
					glog.Errorf("Remove failed: %s", err)
				}
			}
		}
	}

	for p.conns.Len() >= p.maxSize && p.idleNum == 0 {
		p.cond.Wait()
	}

	if p.idleNum > 0 {
		elem := p.conns.Front()
		cn := elem.Value.(*conn)
		if cn.inUse {
			panic("pool: precondition failed")
		}
		cn.inUse = true
		p.conns.MoveToBack(elem)
		p.idleNum--

		p.cond.L.Unlock()
		return cn, false, nil
	}

	if p.conns.Len() < p.maxSize {
		cn, err := p.New()
		if err != nil {
			p.cond.L.Unlock()
			return nil, false, err
		}

		cn.inUse = true
		cn.elem = p.conns.PushBack(cn)

		p.cond.L.Unlock()
		return cn, true, nil
	}

	panic("not reached")
}

func (p *connPool) Put(cn *conn) error {
	if cn.br.Buffered() > 0 {
		b, _ := cn.br.Peek(cn.br.Buffered())
		glog.Errorf("pg: discarding connection that has unread data: %.100q", b)
		return nil
	}

	cn.buf.Reset()
	if p.idleTimeout > 0 {
		cn.usedAt = time.Now()
	}

	p.cond.L.Lock()
	if p.closed {
		p.cond.L.Unlock()
		return errClosed
	}
	cn.inUse = false
	p.conns.MoveToFront(cn.elem)
	p.idleNum++
	p.cond.Signal()
	p.cond.L.Unlock()
	return nil
}

func (p *connPool) remove(cn *conn) (err error) {
	if cn != nil {
		err = cn.Close()
	}
	p.conns.Remove(cn.elem)
	cn.elem = nil
	return err
}

func (p *connPool) Remove(cn *conn) error {
	p.cond.L.Lock()
	if p.closed {
		// Noop, connection is already closed.
		p.cond.L.Unlock()
		return nil
	}
	err := p.remove(cn)
	p.cond.Signal()
	p.cond.L.Unlock()
	return err
}

// Returns number of idle connections.
func (p *connPool) Len() int {
	defer p.cond.L.Unlock()
	p.cond.L.Lock()
	return p.idleNum
}

// Returns number of connections in the pool.
func (p *connPool) Size() int {
	defer p.cond.L.Unlock()
	p.cond.L.Lock()
	return p.conns.Len()
}

func (p *connPool) Close() error {
	defer p.cond.L.Unlock()
	p.cond.L.Lock()

	if p.closed {
		return errClosed
	}
	p.closed = true
	var retErr error
	for e := p.conns.Front(); e != nil; e = e.Next() {
		cn := e.Value.(*conn)
		if err := cn.Close(); err != nil {
			glog.Errorf("cn.Close failed: %s", err)
			retErr = err
		}
		cn.elem = nil
	}
	p.conns = nil
	return retErr
}
<|endoftext|>


<commit_before>package pool

import (
	"fmt"
	"reflect"
	"runtime"
	"sync"
)

const (
	errRecoveryString = "recovering from panic: %+v\nStack Trace:\n %s"
)

// ConsumerHook type is a function that is called during the consumer startup
// and the return value is added to each Job just prior to firing off the job.
// This is good for say creating a database connection for every job to use but
// not having more than there are consumers.
type ConsumerHook func() interface{}

// ErrRecovery contains the error when a consumer goroutine needed to be recovers
type ErrRecovery struct {
	s string
}

// Error prints recovery error
func (e *ErrRecovery) Error() string {
	return e.s
}

// Pool Contains all information for the pool instance
type Pool struct {
	jobs         chan *Job
	results      chan interface{}
	cancel       chan struct{}
	wg           *sync.WaitGroup
	cancelled    bool
	cancelLock   sync.RWMutex
	consumerHook ConsumerHook
}

// JobFunc is the consumable function/job you wish to run
type JobFunc func(job *Job)

// Job contains all information to run a job
type Job struct {
	fn        JobFunc
	params    []interface{}
	hookParam interface{}
	pool      *Pool
}

// HookParam returns the value, if any, set by the ConsumerHook.
// Example a database connection.
func (j *Job) HookParam() interface{} {
	return j.hookParam
}

// Params returns an array of the params that were passed in during the Queueing of the funciton
func (j *Job) Params() []interface{} {
	return j.params
}

// Cancel is a way to let the pool know, from a job, that it should cancel the rest of the
// jobs to be run. The most likely scenario is because an error occured
func (j *Job) Cancel() {
	j.pool.Cancel()
}

// Return returns the jobs result
func (j *Job) Return(result interface{}) {
	j.pool.results <- result
}

// NewPool initializes and returns a new pool instance
func NewPool(consumers int, jobs int) *Pool {

	p := &Pool{
		wg:      new(sync.WaitGroup),
		jobs:    make(chan *Job, jobs),
		results: make(chan interface{}, jobs),
		cancel:  make(chan struct{}),
	}

	for i := 0; i < consumers; i++ {
		go func(p *Pool) {
			defer func(p *Pool) {
				if err := recover(); err != nil {
					trace := make([]byte, 1<<16)
					n := runtime.Stack(trace, true)
					rerr := &ErrRecovery{
						s: fmt.Sprintf(errRecoveryString, err, trace[:n]),
					}
					p.results <- rerr
					p.Cancel()
					p.wg.Done()
				}
			}(p)

			var consumerParm interface{}

			if p.consumerHook != nil {
				consumerParm = p.consumerHook()
			}

			for {
				select {
				case j := <-p.jobs:
					if reflect.ValueOf(j).IsNil() {
						return
					}

					j.hookParam = consumerParm
					j.fn(j)
					p.wg.Done()
				case <-p.cancel:
					return
				}
			}
		}(p)
	}
	return p
}

// AddConsumerHook registers a Consumer Hook function to be called by every consumer
// and setting the return value on every job prior to running. Use case is for
// reusing database connections.
func (p *Pool) AddConsumerHook(fn ConsumerHook) {
	p.consumerHook = fn
}

func (p *Pool) cancelJobs() {
	for range p.jobs {
		p.wg.Done()
	}
}

// Queue adds a job to be processed and the params to be passed to it.
func (p *Pool) Queue(fn JobFunc, params ...interface{}) {

	p.cancelLock.Lock()
	defer p.cancelLock.Unlock()

	if p.cancelled {
		return
	}

	job := &Job{
		fn:     fn,
		params: params,
		pool:   p,
	}

	p.wg.Add(1)
	p.jobs <- job
}

// Cancel cancels all jobs not already running.
// It can also be called from within a job through the Job object
func (p *Pool) Cancel() {
	close(p.cancel)
	p.cancelLock.Lock()
	p.cancelled = true
	p.cancelLock.Unlock()
	p.cancelJobs()
}

// Results returns the processed job results
func (p *Pool) Results() <-chan interface{} {

	close(p.jobs)

	go func() {
		p.wg.Wait()
		close(p.results)
	}()

	return p.results
}
<commit_msg>fix race condition when using consumer hook<commit_after>package pool

import (
	"fmt"
	"reflect"
	"runtime"
	"sync"
)

const (
	errRecoveryString = "recovering from panic: %+v\nStack Trace:\n %s"
)

// ConsumerHook type is a function that is called during the consumer startup
// and the return value is added to each Job just prior to firing off the job.
// This is good for say creating a database connection for every job to use but
// not having more than there are consumers.
type ConsumerHook func() interface{}

// ErrRecovery contains the error when a consumer goroutine needed to be recovers
type ErrRecovery struct {
	s string
}

// Error prints recovery error
func (e *ErrRecovery) Error() string {
	return e.s
}

// Pool Contains all information for the pool instance
type Pool struct {
	jobs         chan *Job
	results      chan interface{}
	cancel       chan struct{}
	wg           *sync.WaitGroup
	cancelled    bool
	cancelLock   sync.RWMutex
	consumerHook ConsumerHook
	once         sync.Once
	consumers    int
}

// JobFunc is the consumable function/job you wish to run
type JobFunc func(job *Job)

// Job contains all information to run a job
type Job struct {
	fn        JobFunc
	params    []interface{}
	hookParam interface{}
	pool      *Pool
}

// HookParam returns the value, if any, set by the ConsumerHook.
// Example a database connection.
func (j *Job) HookParam() interface{} {
	return j.hookParam
}

// Params returns an array of the params that were passed in during the Queueing of the funciton
func (j *Job) Params() []interface{} {
	return j.params
}

// Cancel is a way to let the pool know, from a job, that it should cancel the rest of the
// jobs to be run. The most likely scenario is because an error occured
func (j *Job) Cancel() {
	j.pool.Cancel()
}

// Return returns the jobs result
func (j *Job) Return(result interface{}) {
	j.pool.results <- result
}

// NewPool initializes and returns a new pool instance
func NewPool(consumers int, jobs int) *Pool {

	p := &Pool{
		wg:        new(sync.WaitGroup),
		jobs:      make(chan *Job, jobs),
		results:   make(chan interface{}, jobs),
		cancel:    make(chan struct{}),
		consumers: consumers,
	}

	return p
}

// AddConsumerHook registers a Consumer Hook function to be called by every consumer
// and setting the return value on every job prior to running. Use case is for
// reusing database connections.
func (p *Pool) AddConsumerHook(fn ConsumerHook) {
	p.consumerHook = fn
}

func (p *Pool) cancelJobs() {
	for range p.jobs {
		p.wg.Done()
	}
}

// Queue adds a job to be processed and the params to be passed to it.
func (p *Pool) Queue(fn JobFunc, params ...interface{}) {

	p.once.Do(func() {
		for i := 0; i < p.consumers; i++ {
			go func(p *Pool) {
				defer func(p *Pool) {
					if err := recover(); err != nil {
						trace := make([]byte, 1<<16)
						n := runtime.Stack(trace, true)
						rerr := &ErrRecovery{
							s: fmt.Sprintf(errRecoveryString, err, trace[:n]),
						}
						p.results <- rerr
						p.Cancel()
						p.wg.Done()
					}
				}(p)

				var consumerParm interface{}

				if p.consumerHook != nil {
					consumerParm = p.consumerHook()
				}

				for {
					select {
					case j := <-p.jobs:
						if reflect.ValueOf(j).IsNil() {
							return
						}

						j.hookParam = consumerParm
						j.fn(j)
						p.wg.Done()
					case <-p.cancel:
						return
					}
				}
			}(p)
		}
	})

	p.cancelLock.Lock()
	defer p.cancelLock.Unlock()

	if p.cancelled {
		return
	}

	job := &Job{
		fn:     fn,
		params: params,
		pool:   p,
	}

	p.wg.Add(1)
	p.jobs <- job

}

// Cancel cancels all jobs not already running.
// It can also be called from within a job through the Job object
func (p *Pool) Cancel() {
	close(p.cancel)
	p.cancelLock.Lock()
	p.cancelled = true
	p.cancelLock.Unlock()
	p.cancelJobs()
}

// Results returns the processed job results
func (p *Pool) Results() <-chan interface{} {

	close(p.jobs)

	go func() {
		p.wg.Wait()
		close(p.results)
	}()

	return p.results
}
<|endoftext|>


<commit_before>// MIT License
//
// Copyright (c) 2017 Ryan Fowler
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package red

import (
	"errors"
	"sync"
	"time"
)

var (
	// ErrTooManyActive represents the error on a Pool when the
	// MaxActiveConns and MaxWaiting limits have been reached.
	ErrTooManyActive = errors.New("red: too many active connections")
	errPoolClosed    = errors.New("red: pool closed")
)

type idleConn struct {
	conn      Conn
	idleSince time.Time
}

// Pool represents a pool of zero or more Redis Conns. All methods on a Pool are
// save to use concurrently.
type Pool struct {
	// Addr represents the address of the Redis server to connect to; Required.
	Addr string
	// Dial is an optional function to create a Conn. The default dial
	// attempts to create a TCP connection to the Redis server with a 30 -
	// second connection timeout.
	Dial func(string) (Conn, error)
	// MaxActiveConns represents the maximum total active connections at a
	// given time. A value <= 0 is no active limit.
	MaxActiveConns int
	// MaxIdleConns represents the maximum number of idle connections at a
	// given time. The default value is 10.
	MaxIdleConns int
	// MaxIdleDuration represents the timeout for an idle connection. By
	// default, there is no idle timeout.
	MaxIdleDuration time.Duration
	// MaxWaiting represents the maximum number of goroutines waiting for a
	// Conn because they are blocked by MaxActiveConns. The default value is
	// no limit. With a value < 0, ErrTooManyActive will be returned whenever
	// MaxActiveConns is reached.
	MaxWaiting int
	// OnReuse is an optional function to intercept an idle Conn before use.
	// If OnReuse returns false, the Conn will be closed and another will be
	// retrieved/created.
	OnReuse func(Conn) bool

	mu           sync.Mutex
	cond         *sync.Cond
	numWaiting   int
	numActive    int
	closed       bool
	chMaxIdleDur chan struct{}
	idleConns    []*idleConn
}

// Status represents the status of a Pool.
type Status struct {
	// Closed is true if the Pool has been closed.
	Closed bool
	// NumActive is the total number of active connections.
	NumActive int
	// NumIdle is the number of idle connections.
	NumIdle int
	// NumWaiting is the total number of connections waiting for an idle
	// connection.
	NumWaiting int
}

// Status returns the current status of the Pool.
func (p *Pool) Status() Status {
	p.mu.Lock()
	s := Status{
		Closed:     p.closed,
		NumActive:  p.numActive,
		NumIdle:    len(p.idleConns),
		NumWaiting: p.numWaiting,
	}
	p.mu.Unlock()
	return s
}

// Close closes the Pool and any of its idle connections. After being closed, a
// pool cannot be used and any attempt to use it will return an error.
func (p *Pool) Close() error {
	p.mu.Lock()
	if p.closed {
		p.mu.Unlock()
		return nil
	}
	p.closed = true

	toClose := make([]Conn, len(p.idleConns))
	for i, c := range p.idleConns {
		toClose[i] = c.conn
		p.idleConns[i] = nil
	}
	p.idleConns = nil

	if p.chMaxIdleDur != nil {
		select {
		case p.chMaxIdleDur <- struct{}{}:
		default:
		}
	}
	p.numActive -= len(toClose)
	if p.cond != nil && p.numWaiting > 0 {
		p.cond.Broadcast()
	}
	p.mu.Unlock()

	for _, c := range toClose {
		c.Close()
	}

	return nil
}

// Conn allows for direct access to a Conn using the provided function. The
// Conn is only valid during the lifetime of the function. Conn will return any
// error returned by fn.
func (p *Pool) Conn(fn func(c Conn) error) error {
	c, err := p.getConn()
	if err != nil {
		return err
	}
	defer p.putConn(c)
	return fn(c)
}

func (p *Pool) getConn() (Conn, error) {
	p.mu.Lock()
	for {
		// Return if pool is closed.
		if p.closed {
			p.mu.Unlock()
			return nil, errPoolClosed
		}

		// Attempt to get an idle conn.
		if len(p.idleConns) > 0 {
			c := p.popIdle()
			p.mu.Unlock()
			if p.OnReuse != nil && !p.OnReuse(c) {
				c.Close()
				p.mu.Lock()
				p.decrActiveLocked(1)
				continue
			}
			return c, nil
		}

		// Check number of active conns.
		if p.MaxActiveConns <= 0 || p.numActive < p.MaxActiveConns {
			break
		}
		if p.cond == nil {
			p.cond = sync.NewCond(&p.mu)
		}

		// Check wait limits.
		if !p.waitOK() {
			p.mu.Unlock()
			return nil, ErrTooManyActive
		}

		p.numWaiting++
		p.cond.Wait()
		p.numWaiting--
	}
	p.numActive++
	p.mu.Unlock()

	// Create and return a new connection.
	c, err := p.dialConn()
	if err != nil {
		p.mu.Lock()
		p.decrActiveLocked(1)
		p.mu.Unlock()
		return nil, err
	}
	return c, nil
}

func (p *Pool) dialConn() (Conn, error) {
	if p.Dial == nil {
		return Dial(p.Addr, defaultDialTimeout)
	}
	return p.Dial(p.Addr)
}

func (p *Pool) popIdle() Conn {
	// TODO (ryanfowler): optimize?
	c := p.idleConns[0]
	copy(p.idleConns, p.idleConns[1:])
	p.idleConns[len(p.idleConns)-1] = nil
	p.idleConns = p.idleConns[:len(p.idleConns)-1]
	return c.conn
}

func (p *Pool) waitOK() bool {
	if p.MaxWaiting < 0 {
		return false
	}
	if p.MaxWaiting > 0 && p.numWaiting >= p.MaxWaiting {
		return false
	}
	return true
}

func (p *Pool) putConn(c Conn) {
	if c == nil {
		return
	}

	p.mu.Lock()

	// Return if the pool is closed or the connection has a fatal error.
	if p.closed || c.Err() != nil {
		p.decrActiveLocked(1)
		p.mu.Unlock()
		c.Close()
		return
	}

	// Check number of idle conns.
	maxIdle := p.MaxIdleConns
	if maxIdle < 1 {
		maxIdle = 10
	}
	if len(p.idleConns) >= maxIdle {
		p.decrActiveLocked(1)
		p.mu.Unlock()
		c.Close()
		return
	}

	// Add to idle conns and optionally start the idle conn worker.
	p.idleConns = append(p.idleConns, &idleConn{conn: c, idleSince: time.Now()})
	p.startMaxIdleWorker()
	p.decrActiveLocked(0)
	p.mu.Unlock()
}

func (p *Pool) decrActiveLocked(n int) {
	p.numActive -= n
	if p.cond != nil && p.numWaiting > 0 {
		p.cond.Signal()
	}
}

func (p *Pool) ExecArray(cmd string, args []interface{}, fn func(*ArrayRes) error) error {
	return p.Conn(func(c Conn) error {
		return p.execArray(c, cmd, args, fn)
	})
}

func (p *Pool) execArray(c Conn, cmd string, args []interface{}, fn func(*ArrayRes) error) error {
	c.Cmd(cmd, args...)
	err := c.Send()
	if err != nil {
		return err
	}
	ar, err := c.ReadArray()
	if err != nil {
		return err
	}
	defer ar.Close()
	return fn(ar)
}

func (p *Pool) ExecStringArr(cmd string, args ...interface{}) ([]string, error) {
	var ss []string
	err := p.Conn(func(c Conn) error {
		var err error
		ss, err = p.execStringArr(c, cmd, args...)
		return err
	})
	return ss, err
}

func (p *Pool) execStringArr(c Conn, cmd string, args ...interface{}) ([]string, error) {
	c.Cmd(cmd, args...)
	err := c.Send()
	if err != nil {
		return nil, err
	}
	return c.ReadStringArr()
}

func (p *Pool) ExecString(cmd string, args ...interface{}) (string, error) {
	var s string
	err := p.Conn(func(c Conn) error {
		var err error
		s, err = p.execString(c, cmd, args...)
		return err
	})
	return s, err
}

func (p *Pool) execString(c Conn, cmd string, args ...interface{}) (string, error) {
	c.Cmd(cmd, args...)
	err := c.Send()
	if err != nil {
		return "", err
	}
	return c.ReadString()
}

func (p *Pool) ExecBytes(cmd string, args ...interface{}) ([]byte, error) {
	var b []byte
	err := p.Conn(func(c Conn) error {
		var err error
		b, err = p.execBytes(c, cmd, args...)
		return err
	})
	return b, err
}

func (p *Pool) execBytes(c Conn, cmd string, args ...interface{}) ([]byte, error) {
	c.Cmd(cmd, args...)
	err := c.Send()
	if err != nil {
		return nil, err
	}
	return c.ReadBytes()
}

func (p *Pool) ExecNullString(cmd string, args ...interface{}) (NullString, error) {
	var ns NullString
	err := p.Conn(func(c Conn) error {
		var err error
		ns, err = p.execNullString(c, cmd, args...)
		return err
	})
	return ns, err
}

func (p *Pool) execNullString(c Conn, cmd string, args ...interface{}) (NullString, error) {
	c.Cmd(cmd, args...)
	err := c.Send()
	if err != nil {
		return NullString{}, err
	}
	return c.ReadNullString()
}

func (p *Pool) ExecInteger(cmd string, args ...interface{}) (int64, error) {
	var i int64
	err := p.Conn(func(c Conn) error {
		var err error
		i, err = p.execInteger(c, cmd, args...)
		return err
	})
	return i, err
}

func (p *Pool) execInteger(c Conn, cmd string, args ...interface{}) (int64, error) {
	c.Cmd(cmd, args...)
	err := c.Send()
	if err != nil {
		return 0, err
	}
	return c.ReadInteger()
}

func (p *Pool) startMaxIdleWorker() {
	if p.MaxIdleDuration > 0 && p.chMaxIdleDur == nil && len(p.idleConns) > 0 {
		p.chMaxIdleDur = make(chan struct{}, 1)
		go p.maxIdleWorker()
	}
}

func (p *Pool) maxIdleWorker() {
	dur := maxDuration(p.MaxIdleDuration, time.Second)
	t := time.NewTimer(dur)
	for {
		select {
		case <-t.C:
		case <-p.chMaxIdleDur:
		}

		p.mu.Lock()

		if p.closed || len(p.idleConns) == 0 {
			p.chMaxIdleDur = nil
			p.mu.Unlock()
			t.Stop()
			return
		}

		var toClose []Conn
		expiry := time.Now().Add(-dur)
		for len(p.idleConns) > 0 {
			c := p.idleConns[0]
			if c.idleSince.After(expiry) {
				break
			}
			toClose = append(toClose, c.conn)
			copy(p.idleConns, p.idleConns[1:])
			p.idleConns[len(p.idleConns)-1] = nil
			p.idleConns = p.idleConns[:len(p.idleConns)-1]

		}
		p.numActive -= len(toClose)
		if p.cond != nil {
			for i := 0; i < len(toClose) && i < p.numWaiting; i++ {
				p.cond.Signal()
			}
		}
		p.mu.Unlock()

		for _, c := range toClose {
			c.Close()
		}

		t.Reset(dur)
	}
}

func maxDuration(a, b time.Duration) time.Duration {
	if a > b {
		return a
	}
	return b
}
<commit_msg>Add NetTimeout to Pool<commit_after>// MIT License
//
// Copyright (c) 2017 Ryan Fowler
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package red

import (
	"errors"
	"sync"
	"time"
)

var (
	// ErrTooManyActive represents the error on a Pool when the
	// MaxActiveConns and MaxWaiting limits have been reached.
	ErrTooManyActive = errors.New("red: too many active connections")
	errPoolClosed    = errors.New("red: pool closed")
)

type idleConn struct {
	conn      Conn
	idleSince time.Time
}

// Pool represents a pool of zero or more Redis Conns. All methods on a Pool are
// save to use concurrently.
type Pool struct {
	// Addr represents the address of the Redis server to connect to; Required.
	Addr string

	// Dial is an optional function to create a Conn. The default dial
	// attempts to create a TCP connection to the Redis server with a 30 -
	// second connection timeout.
	Dial func(string) (Conn, error)

	// MaxActiveConns represents the maximum total active connections at a
	// given time. A value <= 0 is no active limit.
	MaxActiveConns int

	// MaxIdleConns represents the maximum number of idle connections at a
	// given time. The default value is 10.
	MaxIdleConns int

	// MaxIdleDuration represents the timeout for an idle connection. By
	// default, there is no idle timeout.
	MaxIdleDuration time.Duration

	// MaxWaiting represents the maximum number of goroutines waiting for a
	// Conn because they are blocked by MaxActiveConns. The default value is
	// no limit. With a value < 0, ErrTooManyActive will be returned whenever
	// MaxActiveConns is reached.
	MaxWaiting int

	// NetTimeout sets a timeout on a Conn for when all IO operations will
	// return an error. It calls the SetDeadline method on the underlying
	// net.Conn before presenting the Conn for use.
	NetTimeout time.Duration

	// OnReuse is an optional function to intercept an idle Conn before use.
	// If OnReuse returns false, the Conn will be closed and another will be
	// retrieved/created.
	OnReuse func(Conn) bool

	mu           sync.Mutex
	cond         *sync.Cond
	numWaiting   int
	numActive    int
	closed       bool
	chMaxIdleDur chan struct{}
	idleConns    []*idleConn
}

// Status represents the status of a Pool.
type Status struct {
	// Closed is true if the Pool has been closed.
	Closed bool
	// NumActive is the total number of active connections.
	NumActive int
	// NumIdle is the number of idle connections.
	NumIdle int
	// NumWaiting is the total number of connections waiting for an idle
	// connection.
	NumWaiting int
}

// Status returns the current status of the Pool.
func (p *Pool) Status() Status {
	p.mu.Lock()
	s := Status{
		Closed:     p.closed,
		NumActive:  p.numActive,
		NumIdle:    len(p.idleConns),
		NumWaiting: p.numWaiting,
	}
	p.mu.Unlock()
	return s
}

// Close closes the Pool and any of its idle connections. After being closed, a
// pool cannot be used and any attempt to use it will return an error.
func (p *Pool) Close() error {
	p.mu.Lock()
	if p.closed {
		p.mu.Unlock()
		return nil
	}
	p.closed = true

	toClose := make([]Conn, len(p.idleConns))
	for i, c := range p.idleConns {
		toClose[i] = c.conn
		p.idleConns[i] = nil
	}
	p.idleConns = nil

	if p.chMaxIdleDur != nil {
		select {
		case p.chMaxIdleDur <- struct{}{}:
		default:
		}
	}
	p.numActive -= len(toClose)
	if p.cond != nil && p.numWaiting > 0 {
		p.cond.Broadcast()
	}
	p.mu.Unlock()

	for _, c := range toClose {
		c.Close()
	}

	return nil
}

// Conn allows for direct access to a Conn using the provided function. The
// Conn is only valid during the lifetime of the function. Conn will return any
// error returned by fn.
func (p *Pool) Conn(fn func(c Conn) error) error {
	c, err := p.getConn()
	if err != nil {
		return err
	}
	defer p.putConn(c)
	p.setTimeout(c)
	return fn(c)
}

func (p *Pool) getConn() (Conn, error) {
	p.mu.Lock()
	for {
		// Return if pool is closed.
		if p.closed {
			p.mu.Unlock()
			return nil, errPoolClosed
		}

		// Attempt to get an idle conn.
		if len(p.idleConns) > 0 {
			c := p.popIdle()
			p.mu.Unlock()
			if p.OnReuse != nil && !p.OnReuse(c) {
				c.Close()
				p.mu.Lock()
				p.decrActiveLocked(1)
				continue
			}
			return c, nil
		}

		// Check number of active conns.
		if p.MaxActiveConns <= 0 || p.numActive < p.MaxActiveConns {
			break
		}
		if p.cond == nil {
			p.cond = sync.NewCond(&p.mu)
		}

		// Check wait limits.
		if !p.waitOK() {
			p.mu.Unlock()
			return nil, ErrTooManyActive
		}

		p.numWaiting++
		p.cond.Wait()
		p.numWaiting--
	}
	p.numActive++
	p.mu.Unlock()

	// Create and return a new connection.
	c, err := p.dialConn()
	if err != nil {
		p.mu.Lock()
		p.decrActiveLocked(1)
		p.mu.Unlock()
		return nil, err
	}
	return c, nil
}

func (p *Pool) setTimeout(c Conn) {
	if p.NetTimeout > 0 {
		c.NetConn().SetDeadline(time.Now().Add(p.NetTimeout))
	}
}

func (p *Pool) dialConn() (Conn, error) {
	if p.Dial == nil {
		return Dial(p.Addr, defaultDialTimeout)
	}
	return p.Dial(p.Addr)
}

func (p *Pool) popIdle() Conn {
	// TODO (ryanfowler): optimize?
	c := p.idleConns[0]
	copy(p.idleConns, p.idleConns[1:])
	p.idleConns[len(p.idleConns)-1] = nil
	p.idleConns = p.idleConns[:len(p.idleConns)-1]
	return c.conn
}

func (p *Pool) waitOK() bool {
	if p.MaxWaiting < 0 {
		return false
	}
	if p.MaxWaiting > 0 && p.numWaiting >= p.MaxWaiting {
		return false
	}
	return true
}

func (p *Pool) putConn(c Conn) {
	if c == nil {
		return
	}

	p.mu.Lock()

	// Return if the pool is closed or the connection has a fatal error.
	if p.closed || c.Err() != nil {
		p.decrActiveLocked(1)
		p.mu.Unlock()
		c.Close()
		return
	}

	// Check number of idle conns.
	maxIdle := p.MaxIdleConns
	if maxIdle < 1 {
		maxIdle = 10
	}
	if len(p.idleConns) >= maxIdle {
		p.decrActiveLocked(1)
		p.mu.Unlock()
		c.Close()
		return
	}

	// Add to idle conns and optionally start the idle conn worker.
	p.idleConns = append(p.idleConns, &idleConn{conn: c, idleSince: time.Now()})
	p.startMaxIdleWorker()
	p.decrActiveLocked(0)
	p.mu.Unlock()
}

func (p *Pool) decrActiveLocked(n int) {
	p.numActive -= n
	if p.cond != nil && p.numWaiting > 0 {
		p.cond.Signal()
	}
}

func (p *Pool) ExecArray(cmd string, args []interface{}, fn func(*Array) error) error {
	return p.Conn(func(c Conn) error {
		return p.execArray(c, cmd, args, fn)
	})
}

func (p *Pool) execArray(c Conn, cmd string, args []interface{}, fn func(*Array) error) error {
	c.Cmd(cmd, args...)
	err := c.Send()
	if err != nil {
		return err
	}
	ar, err := c.ReadArray()
	if err != nil {
		return err
	}
	defer ar.Close()
	return fn(ar)
}

func (p *Pool) ExecStringArr(cmd string, args ...interface{}) ([]string, error) {
	var ss []string
	err := p.Conn(func(c Conn) error {
		var err error
		ss, err = p.execStringArr(c, cmd, args...)
		return err
	})
	return ss, err
}

func (p *Pool) execStringArr(c Conn, cmd string, args ...interface{}) ([]string, error) {
	c.Cmd(cmd, args...)
	err := c.Send()
	if err != nil {
		return nil, err
	}
	return c.ReadStringArr()
}

func (p *Pool) ExecString(cmd string, args ...interface{}) (string, error) {
	var s string
	err := p.Conn(func(c Conn) error {
		var err error
		s, err = p.execString(c, cmd, args...)
		return err
	})
	return s, err
}

func (p *Pool) execString(c Conn, cmd string, args ...interface{}) (string, error) {
	c.Cmd(cmd, args...)
	err := c.Send()
	if err != nil {
		return "", err
	}
	return c.ReadString()
}

func (p *Pool) ExecBytes(cmd string, args ...interface{}) ([]byte, error) {
	var b []byte
	err := p.Conn(func(c Conn) error {
		var err error
		b, err = p.execBytes(c, cmd, args...)
		return err
	})
	return b, err
}

func (p *Pool) execBytes(c Conn, cmd string, args ...interface{}) ([]byte, error) {
	c.Cmd(cmd, args...)
	err := c.Send()
	if err != nil {
		return nil, err
	}
	return c.ReadBytes()
}

func (p *Pool) ExecNullString(cmd string, args ...interface{}) (NullString, error) {
	var ns NullString
	err := p.Conn(func(c Conn) error {
		var err error
		ns, err = p.execNullString(c, cmd, args...)
		return err
	})
	return ns, err
}

func (p *Pool) execNullString(c Conn, cmd string, args ...interface{}) (NullString, error) {
	c.Cmd(cmd, args...)
	err := c.Send()
	if err != nil {
		return NullString{}, err
	}
	return c.ReadNullString()
}

func (p *Pool) ExecInteger(cmd string, args ...interface{}) (int64, error) {
	var i int64
	err := p.Conn(func(c Conn) error {
		var err error
		i, err = p.execInteger(c, cmd, args...)
		return err
	})
	return i, err
}

func (p *Pool) execInteger(c Conn, cmd string, args ...interface{}) (int64, error) {
	c.Cmd(cmd, args...)
	err := c.Send()
	if err != nil {
		return 0, err
	}
	return c.ReadInteger()
}

func (p *Pool) startMaxIdleWorker() {
	if p.MaxIdleDuration > 0 && p.chMaxIdleDur == nil && len(p.idleConns) > 0 {
		p.chMaxIdleDur = make(chan struct{}, 1)
		go p.maxIdleWorker()
	}
}

func (p *Pool) maxIdleWorker() {
	dur := maxDuration(p.MaxIdleDuration, time.Second)
	t := time.NewTimer(dur)
	for {
		select {
		case <-t.C:
		case <-p.chMaxIdleDur:
		}

		p.mu.Lock()

		if p.closed || len(p.idleConns) == 0 {
			p.chMaxIdleDur = nil
			p.mu.Unlock()
			t.Stop()
			return
		}

		var toClose []Conn
		expiry := time.Now().Add(-dur)
		for len(p.idleConns) > 0 {
			c := p.idleConns[0]
			if c.idleSince.After(expiry) {
				break
			}
			toClose = append(toClose, c.conn)
			copy(p.idleConns, p.idleConns[1:])
			p.idleConns[len(p.idleConns)-1] = nil
			p.idleConns = p.idleConns[:len(p.idleConns)-1]

		}
		p.numActive -= len(toClose)
		if p.cond != nil {
			for i := 0; i < len(toClose) && i < p.numWaiting; i++ {
				p.cond.Signal()
			}
		}
		p.mu.Unlock()

		for _, c := range toClose {
			c.Close()
		}

		t.Reset(dur)
	}
}

func maxDuration(a, b time.Duration) time.Duration {
	if a > b {
		return a
	}
	return b
}
<|endoftext|>


<commit_before>package proxmox

type Pool struct {
	Poolid       string  `json:"poolid"`
	proxmox    ProxMox
}

type PoolList map[string]Pool

type PoolResult struct {
	Data Task `json:"data"`
}
<commit_msg>format.<commit_after>package proxmox

type Pool struct {
	Poolid  string `json:"poolid"`
	proxmox ProxMox
}

type PoolList map[string]Pool

type PoolResult struct {
	Data Task `json:"data"`
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"

	"golang.org/x/crypto/ssh"

	"github.com/docker/docker/pkg/mflag"
	"github.com/fsouza/go-dockerclient"
)

var (
	logger = log.New(os.Stdout, "", log.Ldate|log.Ltime)
)

func main() {

	config := struct {
		ListenAddr  string
		Port        uint
		KeyFile     string
		ShowHelp    bool
		ShowVersion bool
		Host        string
		Cmd         string
	}{}

	mflag.StringVar(&config.Host, []string{"H", "-host"}, "unix:///var/run/docker.sock", "docker host socket")
	mflag.StringVar(&config.ListenAddr, []string{"l", "-listen_addr"}, "0.0.0.0", "Listening Address")
	mflag.UintVar(&config.Port, []string{"p", "-port"}, 2232, "Listening Port")
	mflag.StringVar(&config.KeyFile, []string{"i", "-server_key"}, "/etc/ssh/ssh_host_rsa_key", "Key file for SSH")
	mflag.StringVar(&config.Cmd, []string{"c", "-command"}, "/bin/bash", "default exec command")
	mflag.BoolVar(&config.ShowHelp, []string{"h", "-help"}, false, "Print help and exit")

	// TODO version
	mflag.BoolVar(&config.ShowVersion, []string{"-version"}, false, "Print version and exit")

	mflag.Parse()

	if config.ShowHelp {
		mflag.PrintDefaults()
		return
	}

	client, err := docker.NewClient(config.Host)
	if err != nil {
		logger.Fatalln("Cannot connect to docker %v", err)
	}

	server := &ssh.ServerConfig{
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			// TODO query
			return nil, nil
		},
	}

	privateBytes, err := ioutil.ReadFile(config.KeyFile)
	if err != nil {
		logger.Fatalln(err)
	}

	private, err := ssh.ParsePrivateKey(privateBytes)
	if err != nil {
		logger.Fatalln(err)
	}

	server.AddHostKey(private)

	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.ListenAddr, config.Port))
	if err != nil {
		logger.Fatalln("failed to listen for connection: %v", err)
	}
	defer listener.Close()

	logger.Printf("Docker.sshd started")

	for {
		c, err := listener.Accept()
		if err != nil {
			logger.Printf("failed to accept connection: %v", err)
			continue
		}

		logger.Printf("connection accepted: %v", c.RemoteAddr())

		go func() {
			sshConn, chans, reqs, err := ssh.NewServerConn(c, server)

			if err != nil {
				logger.Printf("failed to establish ssh connection: %v", err)
				return
			}

			exec, err := client.CreateExec(docker.CreateExecOptions{
				Container:    sshConn.User(),
				AttachStdin:  true,
				AttachStdout: true,
				AttachStderr: true,
				Tty:          true,
				Cmd:          []string{config.Cmd},
			})

			if err != nil {
				logger.Printf("failed to create docker exec: %v", err)
				sshConn.Close()
				return
			}

			go handleRequests(reqs)
			// Accept all channels
			go handleChannels(client, exec.ID, chans)
		}()
	}
}

func handleRequests(reqs <-chan *ssh.Request) {
	for req := range reqs {
		if req.Type == "keepalive@openssh.com" {
			req.Reply(true, nil)
			continue
		}
		logger.Printf("recieved out-of-band request: %+v", req)
	}
}

func handleChannels(client *docker.Client, execID string, chans <-chan ssh.NewChannel) {
	// Service the incoming Channel channel.
	for newChannel := range chans {
		// Since we're handling the execution of a shell, we expect a
		// channel type of "session". However, there are also: "x11", "direct-tcpip"
		// and "forwarded-tcpip" channel types.
		if t := newChannel.ChannelType(); t != "session" {
			newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
			continue
		}

		// At this point, we have the opportunity to reject the client's
		// request for another logical connection
		channel, requests, err := newChannel.Accept()
		if err != nil {
			logger.Printf("could not accept channel (%s)", err)
			continue
		}

		go func() {
			err := client.StartExec(execID, docker.StartExecOptions{
				Detach:       false,
				OutputStream: channel,
				ErrorStream:  channel,
				InputStream:  channel,
				RawTerminal:  false,
			})

			// this call block until exec done
			exit_status := 0

			if err != nil {
				exit_status = -1
			}

			channel.SendRequest("exit-status", false, ssh.Marshal(&struct{ uint32 }{uint32(exit_status)}))
			channel.Close()

			logger.Printf("session closed")
		}()

		// Sessions have out-of-band requests such as "shell", "pty-req" and "env"
		// https://tools.ietf.org/html/rfc4254#
		// TODO impl more

		go func(in <-chan *ssh.Request) {
			for req := range in {
				ok := false

				switch req.Type {

				case "shell":
					if len(req.Payload) == 0 {
						ok = true
					}

				case "pty-req":
					// Responding 'ok' here will let the client
					// know we have a pty ready for input
					ok = true

					msg := struct {
						Term   string
						Width  uint32
						Height uint32
					}{}

					ssh.Unmarshal(req.Payload, &msg)

					client.ResizeExecTTY(execID, int(msg.Height), int(msg.Width))

					logger.Printf("pty-req '%v' %v * %v", msg.Term, msg.Height, msg.Width)

				case "window-change":

					msg := struct {
						Width  uint32
						Height uint32
					}{}

					ssh.Unmarshal(req.Payload, &msg)

					client.ResizeExecTTY(execID, int(msg.Height), int(msg.Width))

					logger.Printf("windows-changed %v * %v", msg.Height, msg.Width)

					// find way for this
					//case "env":

					//	msg := struct {
					//		Name  string
					//		Value string
					//	}{}

					//	ssh.Unmarshal(req.Payload, &msg)

					//    fmt.Println(msg)
				default:
					logger.Printf("unhandled req type %v", req.Type)
				}

				if req.WantReply {
					req.Reply(ok, nil)
				}
			}
		}(requests)
	}
}
<commit_msg>add ping warning when starting<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"

	"golang.org/x/crypto/ssh"

	"github.com/docker/docker/pkg/mflag"
	"github.com/fsouza/go-dockerclient"
)

var (
	logger = log.New(os.Stdout, "", log.Ldate|log.Ltime)
)

func main() {

	config := struct {
		ListenAddr     string
		Port           uint
		KeyFile        string
		ShowHelp       bool
		ShowVersion    bool
		DockerEndpoint string
		Cmd            string
	}{}

	mflag.StringVar(&config.DockerEndpoint, []string{"H", "-host"}, "unix:///var/run/docker.sock", "docker host socket")
	mflag.StringVar(&config.ListenAddr, []string{"l", "-listen_addr"}, "0.0.0.0", "Listening Address")
	mflag.UintVar(&config.Port, []string{"p", "-port"}, 2232, "Listening Port")
	mflag.StringVar(&config.KeyFile, []string{"i", "-server_key"}, "/etc/ssh/ssh_host_rsa_key", "Key file for docker-sshd")
	mflag.StringVar(&config.Cmd, []string{"c", "-command"}, "/bin/bash", "default exec command")
	mflag.BoolVar(&config.ShowHelp, []string{"h", "-help"}, false, "Print help and exit")

	// TODO version
	mflag.BoolVar(&config.ShowVersion, []string{"-version"}, false, "Print version and exit")

	mflag.Parse()

	if config.ShowHelp {
		mflag.PrintDefaults()
		return
	}

	client, err := docker.NewClient(config.DockerEndpoint)
	if err != nil {
		logger.Fatalln("Cannot connect to docker %v", err)
	}

	if err := client.Ping(); err != nil {
		logger.Println("WARNING ping docker failed")
	}

	server := &ssh.ServerConfig{
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			// TODO query
			return nil, nil
		},
	}

	privateBytes, err := ioutil.ReadFile(config.KeyFile)
	if err != nil {
		logger.Fatalln(err)
	}

	private, err := ssh.ParsePrivateKey(privateBytes)
	if err != nil {
		logger.Fatalln(err)
	}

	server.AddHostKey(private)

	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.ListenAddr, config.Port))
	if err != nil {
		logger.Fatalln("failed to listen for connection: %v", err)
	}
	defer listener.Close()

	logger.Printf("Docker-sshd started, Listening %s:%d", config.ListenAddr, config.Port)

	for {
		c, err := listener.Accept()
		if err != nil {
			logger.Printf("failed to accept connection: %v", err)
			continue
		}

		logger.Printf("connection accepted: %v", c.RemoteAddr())

		go func() {
			sshConn, chans, reqs, err := ssh.NewServerConn(c, server)

			if err != nil {
				logger.Printf("failed to establish ssh connection: %v", err)
				return
			}

			exec, err := client.CreateExec(docker.CreateExecOptions{
				Container:    sshConn.User(),
				AttachStdin:  true,
				AttachStdout: true,
				AttachStderr: true,
				Tty:          true,
				Cmd:          []string{config.Cmd},
			})

			if err != nil {
				logger.Printf("failed to create docker exec: %v", err)
				sshConn.Close()
				return
			}

			go handleRequests(reqs)
			// Accept all channels
			go handleChannels(client, exec.ID, chans)
		}()
	}
}

func handleRequests(reqs <-chan *ssh.Request) {
	for req := range reqs {
		if req.Type == "keepalive@openssh.com" {
			req.Reply(true, nil)
			continue
		}
		logger.Printf("recieved out-of-band request: %+v", req)
	}
}

func handleChannels(client *docker.Client, execID string, chans <-chan ssh.NewChannel) {
	// Service the incoming Channel channel.
	for newChannel := range chans {
		// Since we're handling the execution of a shell, we expect a
		// channel type of "session". However, there are also: "x11", "direct-tcpip"
		// and "forwarded-tcpip" channel types.
		if t := newChannel.ChannelType(); t != "session" {
			newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
			continue
		}

		// At this point, we have the opportunity to reject the client's
		// request for another logical connection
		channel, requests, err := newChannel.Accept()
		if err != nil {
			logger.Printf("could not accept channel (%s)", err)
			continue
		}

		go func() {
			err := client.StartExec(execID, docker.StartExecOptions{
				Detach:       false,
				OutputStream: channel,
				ErrorStream:  channel,
				InputStream:  channel,
				RawTerminal:  false,
			})

			// this call block until exec done
			exit_status := 0

			if err != nil {
				exit_status = -1
			}

			channel.SendRequest("exit-status", false, ssh.Marshal(&struct{ uint32 }{uint32(exit_status)}))
			channel.Close()

			logger.Printf("session closed")
		}()

		// Sessions have out-of-band requests such as "shell", "pty-req" and "env"
		// https://tools.ietf.org/html/rfc4254#
		// TODO impl more

		go func(in <-chan *ssh.Request) {
			for req := range in {
				ok := false

				switch req.Type {

				case "shell":
					if len(req.Payload) == 0 {
						ok = true
					}

				case "pty-req":
					// Responding 'ok' here will let the client
					// know we have a pty ready for input
					ok = true

					msg := struct {
						Term   string
						Width  uint32
						Height uint32
					}{}

					ssh.Unmarshal(req.Payload, &msg)

					client.ResizeExecTTY(execID, int(msg.Height), int(msg.Width))

					logger.Printf("pty-req '%v' %v * %v", msg.Term, msg.Height, msg.Width)

				case "window-change":

					msg := struct {
						Width  uint32
						Height uint32
					}{}

					ssh.Unmarshal(req.Payload, &msg)

					client.ResizeExecTTY(execID, int(msg.Height), int(msg.Width))

					logger.Printf("windows-changed %v * %v", msg.Height, msg.Width)

					// find way for this
					//case "env":

					//	msg := struct {
					//		Name  string
					//		Value string
					//	}{}

					//	ssh.Unmarshal(req.Payload, &msg)

					//    fmt.Println(msg)
				default:
					logger.Printf("unhandled req type %v", req.Type)
				}

				if req.WantReply {
					req.Reply(ok, nil)
				}
			}
		}(requests)
	}
}
<|endoftext|>


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

// +build host coreos src

package main

import (
	"fmt"
	"strings"
	"testing"

	"github.com/coreos/rkt/tests/testutils"
)

func TestAceValidator(t *testing.T) {
	newStringSet := func(strs ...string) map[string]struct{} {
		m := make(map[string]struct{}, len(strs))
		for _, s := range strs {
			m[s] = struct{}{}
		}
		return m
	}
	expected := []map[string]struct{}{
		newStringSet("prestart"),
		newStringSet("main", "sidekick"),
		newStringSet("poststop"),
	}
	pattern := `ace-validator\[\d+\]: ([[:alpha:]]+) OK`

	ctx := testutils.NewRktRunCtx()
	defer ctx.Cleanup()

	if err := ctx.LaunchMDS(); err != nil {
		t.Fatalf("Cannot launch metadata service: %v", err)
	}

	aceMain := testutils.GetValueFromEnvOrPanic("RKT_ACE_MAIN_IMAGE")
	aceSidekick := testutils.GetValueFromEnvOrPanic("RKT_ACE_SIDEKICK_IMAGE")

	rktArgs := fmt.Sprintf("--debug --insecure-options=image run --mds-register --volume database,kind=empty %s %s",
		aceMain, aceSidekick)
	rktCmd := fmt.Sprintf("%s %s", ctx.Cmd(), rktArgs)

	child := spawnOrFail(t, rktCmd)
	defer waitOrFail(t, child, 0)

	out := ""
	for _, set := range expected {
		for len(set) > 0 {
			results, o, err := expectRegexWithOutput(child, pattern)
			out += o
			if err != nil {
				var keys []string
				for k := range set {
					keys = append(keys, fmt.Sprintf("%q", k))
				}
				ex := strings.Join(keys, " or ")
				t.Fatalf("Expected %s, but not found: %v\nOutput: %v", ex, err, out)
			}
			if len(results) != 2 {
				t.Fatalf("Unexpected regex results, expected a whole match and one submatch, got %#v", results)
			}
			aceStage := results[1]
			if _, ok := set[aceStage]; ok {
				t.Logf("Got expected ACE stage %q", aceStage)
				delete(set, aceStage)
			} else {
				t.Logf("Ignoring unknown ACE stage %q", aceStage)
			}
		}
	}
}
<commit_msg>functional tests: ignore ACE validator test<commit_after>// Copyright 2015 The rkt Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build ignore

package main

import (
	"fmt"
	"strings"
	"testing"

	"github.com/coreos/rkt/tests/testutils"
)

func TestAceValidator(t *testing.T) {
	newStringSet := func(strs ...string) map[string]struct{} {
		m := make(map[string]struct{}, len(strs))
		for _, s := range strs {
			m[s] = struct{}{}
		}
		return m
	}
	expected := []map[string]struct{}{
		newStringSet("prestart"),
		newStringSet("main", "sidekick"),
		newStringSet("poststop"),
	}
	pattern := `ace-validator\[\d+\]: ([[:alpha:]]+) OK`

	ctx := testutils.NewRktRunCtx()
	defer ctx.Cleanup()

	if err := ctx.LaunchMDS(); err != nil {
		t.Fatalf("Cannot launch metadata service: %v", err)
	}

	aceMain := testutils.GetValueFromEnvOrPanic("RKT_ACE_MAIN_IMAGE")
	aceSidekick := testutils.GetValueFromEnvOrPanic("RKT_ACE_SIDEKICK_IMAGE")

	rktArgs := fmt.Sprintf("--debug --insecure-options=image run --mds-register --volume database,kind=empty %s %s",
		aceMain, aceSidekick)
	rktCmd := fmt.Sprintf("%s %s", ctx.Cmd(), rktArgs)

	child := spawnOrFail(t, rktCmd)
	defer waitOrFail(t, child, 0)

	out := ""
	for _, set := range expected {
		for len(set) > 0 {
			results, o, err := expectRegexWithOutput(child, pattern)
			out += o
			if err != nil {
				var keys []string
				for k := range set {
					keys = append(keys, fmt.Sprintf("%q", k))
				}
				ex := strings.Join(keys, " or ")
				t.Fatalf("Expected %s, but not found: %v\nOutput: %v", ex, err, out)
			}
			if len(results) != 2 {
				t.Fatalf("Unexpected regex results, expected a whole match and one submatch, got %#v", results)
			}
			aceStage := results[1]
			if _, ok := set[aceStage]; ok {
				t.Logf("Got expected ACE stage %q", aceStage)
				delete(set, aceStage)
			} else {
				t.Logf("Ignoring unknown ACE stage %q", aceStage)
			}
		}
	}
}
<|endoftext|>


<commit_before>package rpm

import (
	"errors"
	"strings"

	log "github.com/Sirupsen/logrus"

	common "github.com/dvonthenen/goxplatform/inst/common"
	run "github.com/dvonthenen/goxplatform/run"
)

var (
	//ErrExecEmptyOutput failed to generate any output
	ErrExecEmptyOutput = errors.New("Failed to generate any output")
)

//Rpm implementation for the Rpm package manager
type Rpm struct {
	run *run.Run
}

//NewRpm generates a Rpm object
func NewRpm() *Rpm {
	myRun := run.NewRun()
	myRpm := &Rpm{
		run: myRun,
	}
	return myRpm
}

//IsInstalled returns if the package is installed
func (rpm *Rpm) IsInstalled(packageName string) error {
	log.Debugln("IsInstalled ENTER")
	log.Debugln("packageName:", packageName)

	_, err := rpm.GetInstalledVersion(packageName, false)
	if err != nil {
		log.Debugln("Package", packageName, "IS NOT installed")
		log.Debugln("IsInstalled LEAVE")
		return err
	}

	log.Debugln("Package", packageName, "IS installed")
	log.Debugln("IsInstalled LEAVE")
	return nil
}

func correctVersionFromRpm(version string) string {
	if len(version) == 0 {
		return ""
	}

	index := strings.Index(version, "-")
	if index == -1 {
		return version
	}

	fixedVersion := version[:index]
	return fixedVersion
}

//GetInstalledVersion returns the version of the installed package
func (rpm *Rpm) GetInstalledVersion(packageName string, parseVersion bool) (string, error) {
	log.Debugln("GetInstalledVersion ENTER")
	log.Debugln("packageName:", packageName)

	cmdline1 := "rpm -qi " + packageName + " | grep Version | sed -n -e 's/^.*Version.*: //p'"
	output1, errCmd1 := rpm.run.CommandOutput(cmdline1)
	if errCmd1 != nil {
		log.Debugln("runCommandOutput Failed:", errCmd1)
		log.Debugln("GetInstalledVersion LEAVE")
		return "", errCmd1
	}
	if len(output1) == 0 {
		log.Debugln("Output1 length is empty")
		log.Debugln("GetInstalledVersion LEAVE")
		return "", ErrExecEmptyOutput
	}

	cmdline2 = "rpm -qi " + packageName + " | grep Release | sed -n -e 's/^.*Release.*: //p'"
	output2, errCmd2 := rpm.run.CommandOutput(cmdline2)
	if errCmd2 != nil {
		log.Debugln("runCommandOutput Failed:", errCmd2)
		log.Debugln("GetInstalledVersion LEAVE")
		return "", errCmd2
	}
	if len(output2) == 0 {
		log.Debugln("Output2 length is empty")
		log.Debugln("GetInstalledVersion LEAVE")
		return "", ErrExecEmptyOutput
	}

	output := output1 + "-" + output2

	if strings.Contains(output1, "is not installed") ||
		strings.Contains(output2, "is not installed") {
		log.Warnln("Package", packageName, "is not installed. Blanking the output.")
		output = ""
	}

	//this is for REX-Ray and DVDCLI that only use the format 0.2.0
	//0.2.0-1 -> 0.2.0
	version := correctVersionFromRpm(output)

	//use the original string but remove anything but the version
	//2.0-10000.2072.el7 -> 2.0-10000.2072
	if parseVersion {
		myVersion, errParse := common.ParseVersionFromFilename(output)
		if errParse != nil {
			log.Debugln("ParseVersionFromFilename Failed:", errParse)
			log.Debugln("GetInstalledVersion LEAVE")
			return "", errParse
		}
		version = myVersion
	}

	log.Debugln("GetInstalledVersion Found:", version)
	log.Debugln("GetInstalledVersion LEAVE")

	return version, nil
}
<commit_msg>Fxi cmd declaration<commit_after>package rpm

import (
	"errors"
	"strings"

	log "github.com/Sirupsen/logrus"

	common "github.com/dvonthenen/goxplatform/inst/common"
	run "github.com/dvonthenen/goxplatform/run"
)

var (
	//ErrExecEmptyOutput failed to generate any output
	ErrExecEmptyOutput = errors.New("Failed to generate any output")
)

//Rpm implementation for the Rpm package manager
type Rpm struct {
	run *run.Run
}

//NewRpm generates a Rpm object
func NewRpm() *Rpm {
	myRun := run.NewRun()
	myRpm := &Rpm{
		run: myRun,
	}
	return myRpm
}

//IsInstalled returns if the package is installed
func (rpm *Rpm) IsInstalled(packageName string) error {
	log.Debugln("IsInstalled ENTER")
	log.Debugln("packageName:", packageName)

	_, err := rpm.GetInstalledVersion(packageName, false)
	if err != nil {
		log.Debugln("Package", packageName, "IS NOT installed")
		log.Debugln("IsInstalled LEAVE")
		return err
	}

	log.Debugln("Package", packageName, "IS installed")
	log.Debugln("IsInstalled LEAVE")
	return nil
}

func correctVersionFromRpm(version string) string {
	if len(version) == 0 {
		return ""
	}

	index := strings.Index(version, "-")
	if index == -1 {
		return version
	}

	fixedVersion := version[:index]
	return fixedVersion
}

//GetInstalledVersion returns the version of the installed package
func (rpm *Rpm) GetInstalledVersion(packageName string, parseVersion bool) (string, error) {
	log.Debugln("GetInstalledVersion ENTER")
	log.Debugln("packageName:", packageName)

	cmdline1 := "rpm -qi " + packageName + " | grep Version | sed -n -e 's/^.*Version.*: //p'"
	output1, errCmd1 := rpm.run.CommandOutput(cmdline1)
	if errCmd1 != nil {
		log.Debugln("runCommandOutput Failed:", errCmd1)
		log.Debugln("GetInstalledVersion LEAVE")
		return "", errCmd1
	}
	if len(output1) == 0 {
		log.Debugln("Output1 length is empty")
		log.Debugln("GetInstalledVersion LEAVE")
		return "", ErrExecEmptyOutput
	}

	cmdline2 := "rpm -qi " + packageName + " | grep Release | sed -n -e 's/^.*Release.*: //p'"
	output2, errCmd2 := rpm.run.CommandOutput(cmdline2)
	if errCmd2 != nil {
		log.Debugln("runCommandOutput Failed:", errCmd2)
		log.Debugln("GetInstalledVersion LEAVE")
		return "", errCmd2
	}
	if len(output2) == 0 {
		log.Debugln("Output2 length is empty")
		log.Debugln("GetInstalledVersion LEAVE")
		return "", ErrExecEmptyOutput
	}

	output := output1 + "-" + output2

	if strings.Contains(output1, "is not installed") ||
		strings.Contains(output2, "is not installed") {
		log.Warnln("Package", packageName, "is not installed. Blanking the output.")
		output = ""
	}

	//this is for REX-Ray and DVDCLI that only use the format 0.2.0
	//0.2.0-1 -> 0.2.0
	version := correctVersionFromRpm(output)

	//use the original string but remove anything but the version
	//2.0-10000.2072.el7 -> 2.0-10000.2072
	if parseVersion {
		myVersion, errParse := common.ParseVersionFromFilename(output)
		if errParse != nil {
			log.Debugln("ParseVersionFromFilename Failed:", errParse)
			log.Debugln("GetInstalledVersion LEAVE")
			return "", errParse
		}
		version = myVersion
	}

	log.Debugln("GetInstalledVersion Found:", version)
	log.Debugln("GetInstalledVersion LEAVE")

	return version, nil
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
)

func TestIdGenerator(t *testing.T) {
	const (
		l = 5
		n = 10000
	)

	for j := 0; j < l; j++ {
		idGen := newIdGenerator(l)

		m := map[string]bool{}
		for i := 0; i < n; i++ {
			id, err := idGen.newId()
			if err != nil {
				t.Fatal(err)
			} else if m[id] {
				t.Error(id)
			}
			m[id] = true
		}
	}
}
<commit_msg>ID 生成器の並列使用テストを追加<commit_after>package main

import (
	"sync"
	"testing"
)

func TestIdGenerator(t *testing.T) {
	const (
		l = 5
		n = 10000
	)

	for j := 0; j < l; j++ {
		idGen := newIdGenerator(l)

		m := map[string]bool{}
		for i := 0; i < n; i++ {
			id, err := idGen.newId()
			if err != nil {
				t.Fatal(err)
			} else if m[id] {
				t.Error(id)
			}
			m[id] = true
		}
	}
}

func TestIdGeneratorConcurrent(t *testing.T) {
	const (
		p = 10
		n = 5000
	)

	idGen := newIdGenerator(0)

	glbl := map[string]bool{}
	var lock sync.Mutex
	errCh := make(chan int, p)
	endCh := make(chan struct{}, p)

	for j := 0; j < p; j++ {
		go func(self int) {
			defer func() { endCh <- struct{}{} }()

			// まずローカルで ID 生成する。
			buff := map[string]bool{}
			for i := 0; i < n; i++ {
				id, err := idGen.newId()
				if err != nil {
					t.Error(err)
					errCh <- self
					return
				} else if buff[id] {
					errCh <- self
					return
				}
				buff[id] = true
			}

			// グローバルに ID が重複していないか検査。
			lock.Lock()
			defer lock.Unlock()
			for id := range buff {
				if glbl[id] {
					errCh <- self
					return
				}
				glbl[id] = true
			}
		}(j)
	}

	for i := 0; i < p; i++ {
		<-endCh
	}
	for ok := false; ok; {
		select {
		case pid := <-errCh:
			t.Error(pid)
		default:
			ok = true
		}
	}
	if len(glbl) != p*n {
		t.Error(len(glbl), p*n)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	log "github.com/cihub/seelog"
	"github.com/crufter/borg/types"
	httpr "github.com/julienschmidt/httprouter"
	"github.com/olivere/elastic"
	"net/http"
	"reflect"
	"strconv"
)

var (
	client *elastic.Client
)

func init() {
	cl, err := elastic.NewClient(elastic.SetSniff(false), elastic.SetURL("http://127.0.0.1:9200"))
	if err != nil {
		panic(err)
	}
	client = cl
}

func main() {
	r := httpr.New()
	r.GET("/v1/query", query)
	log.Info("Starting http server")
	log.Critical(http.ListenAndServe(fmt.Sprintf(":%v", 9992), r))
}

func query(w http.ResponseWriter, r *http.Request, p httpr.Params) {
	size := 5
	s, err := strconv.ParseInt(r.FormValue("l"), 10, 32)
	if err == nil && s > 0 {
		size = int(s)
	}
	if size > 50 {
		size = 50
	}
	q := r.FormValue("q")
	if r.FormValue("p") == "true" {
		log.Info("Private query with size '%v'", size)
	} else {
		log.Infof("Querying '%v' with size '%v'", q, size)
	}
	res, err := client.Search().Index("borg").Type("problem").From(0).Size(size).Query(
		elastic.NewQueryStringQuery(q)).Do()
	if err != nil {
		panic(err)
	}
	all := []types.Problem{}
	var ttyp types.Problem
	for _, item := range res.Each(reflect.TypeOf(ttyp)) {
		if t, ok := item.(types.Problem); ok {
			all = append(all, t)
		}
	}
	bs, err := json.Marshal(all)
	if err != nil {
		panic(err)
	}
	fmt.Fprint(w, string(bs))
}
<commit_msg>Correct typo in log text<commit_after>package main

import (
	"encoding/json"
	"fmt"
	log "github.com/cihub/seelog"
	"github.com/crufter/borg/types"
	httpr "github.com/julienschmidt/httprouter"
	"github.com/olivere/elastic"
	"net/http"
	"reflect"
	"strconv"
)

var (
	client *elastic.Client
)

func init() {
	cl, err := elastic.NewClient(elastic.SetSniff(false), elastic.SetURL("http://127.0.0.1:9200"))
	if err != nil {
		panic(err)
	}
	client = cl
}

func main() {
	r := httpr.New()
	r.GET("/v1/query", query)
	log.Info("Starting http server")
	log.Critical(http.ListenAndServe(fmt.Sprintf(":%v", 9992), r))
}

func query(w http.ResponseWriter, r *http.Request, p httpr.Params) {
	size := 5
	s, err := strconv.ParseInt(r.FormValue("l"), 10, 32)
	if err == nil && s > 0 {
		size = int(s)
	}
	if size > 50 {
		size = 50
	}
	q := r.FormValue("q")
	if r.FormValue("p") == "true" {
		log.Infof("Private query with size '%v'", size)
	} else {
		log.Infof("Querying '%v' with size '%v'", q, size)
	}
	res, err := client.Search().Index("borg").Type("problem").From(0).Size(size).Query(
		elastic.NewQueryStringQuery(q)).Do()
	if err != nil {
		panic(err)
	}
	all := []types.Problem{}
	var ttyp types.Problem
	for _, item := range res.Each(reflect.TypeOf(ttyp)) {
		if t, ok := item.(types.Problem); ok {
			all = append(all, t)
		}
	}
	bs, err := json.Marshal(all)
	if err != nil {
		panic(err)
	}
	fmt.Fprint(w, string(bs))
}
<|endoftext|>


<commit_before>package geom

import (
	"fmt"
	"math"
)

// The Rect type represents a 2D rectangle made of the coordinates of an origin
// point, and width and height dimensions.
type Rect struct {
	X float64
	Y float64
	W float64
	H float64
}

// MakeRect constructs a Rect value from an origin and dimensions provided as a
// Point and Size value..
func MakeRect(p Point, s Size) Rect {
	return Rect{
		X: p.X,
		Y: p.Y,
		W: s.W,
		H: s.H,
	}
}

// SetOrigin changes the origin of the rectangle instance it is called on to the
// Point passed as argument.
func (r *Rect) SetOrigin(p Point) {
	r.X = p.X
	r.Y = p.Y
}

// SetSize changes the dimensions of the rectangle instance it is called on to
// the Size passed as argument.
func (r *Rect) SetSize(s Size) {
	r.W = s.W
	r.H = s.H
}

// The Grow method modifies the origin and dimensions of the rectangle it is
// called on by the value passed as argument.
func (r *Rect) Grow(k float64) {
	r.X -= k
	r.Y -= k

	k += k

	r.W += k
	r.H += k
}

// Origin returns the current origin of the rectangle as a Point value.
func (r Rect) Origin() Point {
	return Point{
		X: r.X,
		Y: r.Y,
	}
}

// Center returns the current center of the rectangle as a Point value.
func (r Rect) Center() Point {
	return Point{
		X: r.X + (r.W / 2),
		Y: r.Y + (r.H / 2),
	}
}

// Tip returns the 'bottom-right' point of the rectangle as a Point value.
func (r Rect) Tip() Point {
	return Point{
		X: r.X + r.W,
		Y: r.Y + r.H,
	}
}

// Size returns the dimensions of the rectangle as a Size value.
func (r Rect) Size() Size {
	return Size{
		W: r.W,
		H: r.H,
	}
}

// The Abs method transforms returns a rectangle equivalent to the one it is
// called on where negative components of the dimensions have been converted
// to positive values.
func (r Rect) Abs() Rect {
	if r.W < 0 {
		r.X += r.W
		r.W = -r.W
	}

	if r.H < 0 {
		r.Y += r.H
		r.H = -r.H
	}

	return r
}

// The Intersect method computes the intersection of the rectangle it's called
// on with the one passed as argument, returning the result.
//
// The intersection is the area that is covered by both rectangles, which may be
// a zero-value if the two don't overlap.
func (r0 Rect) Intersect(r1 Rect) Rect {
	r0 = r0.Abs()
	r1 = r1.Abs()

	x1 := math.Max(r0.X, r1.X)
	y1 := math.Max(r0.Y, r1.Y)

	x2 := math.Min(r0.X+r0.W, r1.X+r1.W)
	y2 := math.Min(r0.Y+r0.H, r1.Y+r1.H)

	if x1 > x2 || y1 > y2 {
		return Rect{}
	}

	return Rect{
		X: x1,
		Y: y1,
		W: x2 - x1,
		H: y2 - y1,
	}
}

// The Merge method computes and returns the smallest rectangle that includes
// both the one it is called on and the one passed as argument.
func (r0 Rect) Merge(r1 Rect) Rect {
	r0 = r0.Abs()
	r1 = r1.Abs()

	x1 := math.Min(r0.X, r1.X)
	y1 := math.Min(r0.Y, r1.Y)

	x2 := math.Max(r0.X+r0.W, r1.X+r1.W)
	y2 := math.Max(r0.Y+r0.H, r1.Y+r1.H)

	return Rect{
		X: x1,
		Y: y1,
		W: x2 - x1,
		H: y2 - y1,
	}
}

// The Zero method checks whether the rectangle it is called on is the
// zero-value.
func (r Rect) Zero() bool {
	return r.Origin().Zero() && r.Size().Zero()
}

// The Empty method checks whether the rectangle is empty, which means it has a
// zero area.
func (r Rect) Empty() bool {
	return r.Size().Empty()
}

// The Area method computes and returns the area of the rectangle it is called on
// (width x height).
func (r Rect) Area() float64 {
	return r.Size().Area()
}

// ContainsPoint checks whether the point passed as argument is contained in the
// rectangle it's called on, returning true when that's the case, false otherwise.
func (r Rect) ContainsPoint(p Point) bool {
	return r.X <= p.X && (r.X+r.W) > p.X && r.Y <= p.Y && (r.Y+r.H) > p.Y
}

// ContainsRect checks whether the rectangle passed as argument is contained in
// the one it's called on, returning true when that's the case, false otherwise.
func (r0 Rect) ContainsRect(r1 Rect) bool {
	return r0.X <= r1.X && (r0.X+r0.W) >= (r1.X+r1.W) && r0.Y <= r1.Y && (r0.Y+r0.H) >= (r1.Y+r1.H)
}

// The String method returns a human-readable representation of the rectangle.
func (r Rect) String() string {
	return fmt.Sprintf("{ %.6g, %.6g, %.6g, %.6g }", r.X, r.Y, r.W, r.H)
}

// The Path method satisfies the Shape interface, allowing Rect values to be
// used with programs that manipulate shapes.
func (r Rect) Path() Path {
	return AppendRect(MakePath(5), r)
}

// MergeRect merges a rectangle into an existing list of other rectangles,
// returing the potentially modified slice.
//
// Merging doesn't always append the given rectangle to the list, it won't be
// added if another rectangel in the list already contains the one passed as
// argument for example. A program should consider the list of rectangles passed
// to and returned by the function as the optimal list of rectangle that would
// contain all the merged rectangles. This is particulary useful when keeping
// track of 'dirty' areas in a way that would minimize the 'dirty' surface.
func MergeRect(list []Rect, rect Rect) []Rect {
	// First we check if one of the rectangles in the slice contains the one we
	// are trying to add. If that's the case there's nothing we need to do, the
	// rectangle is already contained in the slice.
	for _, r := range list {
		if r.ContainsRect(rect) {
			return list
		}
	}

	// Look for rectangles that may intersect with the one we are trying to add,
	// if one is found we merge them and rebuild the slice in case it created
	// new intersections.
	for i, r := range list {
		if !r.Intersect(rect).Empty() {
			s := append(make([]Rect, 0, len(list)), r.Merge(rect))

			for j, r := range list {
				if i != j {
					s = MergeRect(s, r)
				}
			}

			return s
		}
	}

	// TOOD: more clever merging policies?
	// -----------------------------------
	//
	// - don't merge if the resulting area is greater than keeping rectangles
	// - look for the merge that creates the smallest area
	// - optimize memory allocation, only recreate the list if needed
	// - sort the list by areas in descending order to improve the fast path
	//
	// While these merges could greatly reduce the areas being redrawn they
	// could also be expansive to compute for potentially little gain due to
	// the way widgets are laid out (parents contain children).
	// It is also possible that generating more redrawing calls may be slower
	// to render than asking the window system to redraw a bigger area once.

	// If none of the merging policies have matched we simply append it to the
	// slice.
	return append(list, rect)
}
<commit_msg>update rect documentation<commit_after>package geom

import (
	"fmt"
	"math"
)

// The Rect type represents a 2D rectangle made of the coordinates of an origin
// point, and width and height dimensions.
type Rect struct {
	X float64
	Y float64
	W float64
	H float64
}

// MakeRect constructs a Rect value from an origin and dimensions provided as a
// Point and Size value..
func MakeRect(p Point, s Size) Rect {
	return Rect{
		X: p.X,
		Y: p.Y,
		W: s.W,
		H: s.H,
	}
}

// SetOrigin changes the origin of the rectangle instance it is called on to the
// Point passed as argument.
func (r *Rect) SetOrigin(p Point) {
	r.X = p.X
	r.Y = p.Y
}

// SetSize changes the dimensions of the rectangle instance it is called on to
// the Size passed as argument.
func (r *Rect) SetSize(s Size) {
	r.W = s.W
	r.H = s.H
}

// Grow modifies the origin and dimensions of the rectangle it is called on by
// the value passed as argument.
func (r *Rect) Grow(k float64) {
	r.X -= k
	r.Y -= k

	k += k

	r.W += k
	r.H += k
}

// Origin returns the current origin of the rectangle as a Point value.
func (r Rect) Origin() Point {
	return Point{
		X: r.X,
		Y: r.Y,
	}
}

// Center returns the current center of the rectangle as a Point value.
func (r Rect) Center() Point {
	return Point{
		X: r.X + (r.W / 2),
		Y: r.Y + (r.H / 2),
	}
}

// Tip returns the 'bottom-right' point of the rectangle as a Point value.
func (r Rect) Tip() Point {
	return Point{
		X: r.X + r.W,
		Y: r.Y + r.H,
	}
}

// Size returns the dimensions of the rectangle as a Size value.
func (r Rect) Size() Size {
	return Size{
		W: r.W,
		H: r.H,
	}
}

// Abs transforms returns a rectangle equivalent to the one it is called on
// where negative components of the dimensions have been converted to positive
// values.
func (r Rect) Abs() Rect {
	if r.W < 0 {
		r.X += r.W
		r.W = -r.W
	}

	if r.H < 0 {
		r.Y += r.H
		r.H = -r.H
	}

	return r
}

// Intersect computes the intersection of the rectangle it's called on with the
// one passed as argument, returning the result.
//
// The intersection is the area that is covered by both rectangles, which may be
// a zero-value if the two don't overlap.
func (r Rect) Intersect(r1 Rect) Rect {
	r = r.Abs()
	r1 = r1.Abs()

	x1 := math.Max(r.X, r1.X)
	y1 := math.Max(r.Y, r1.Y)

	x2 := math.Min(r.X+r.W, r1.X+r1.W)
	y2 := math.Min(r.Y+r.H, r1.Y+r1.H)

	if x1 > x2 || y1 > y2 {
		return Rect{}
	}

	return Rect{
		X: x1,
		Y: y1,
		W: x2 - x1,
		H: y2 - y1,
	}
}

// Merge computes and returns the smallest rectangle that includes both the one
// it is called on and the one passed as argument.
func (r Rect) Merge(r1 Rect) Rect {
	r = r.Abs()
	r1 = r1.Abs()

	x1 := math.Min(r.X, r1.X)
	y1 := math.Min(r.Y, r1.Y)

	x2 := math.Max(r.X+r.W, r1.X+r1.W)
	y2 := math.Max(r.Y+r.H, r1.Y+r1.H)

	return Rect{
		X: x1,
		Y: y1,
		W: x2 - x1,
		H: y2 - y1,
	}
}

// Zero checks whether the rectangle it is called on is the zero-value.
func (r Rect) Zero() bool {
	return r.Origin().Zero() && r.Size().Zero()
}

// Empty method checks whether the rectangle is empty, which means it has a zero
// area.
func (r Rect) Empty() bool {
	return r.Size().Empty()
}

// Area computes and returns the area of the rectangle it is called on
// (width x height).
func (r Rect) Area() float64 {
	return r.Size().Area()
}

// ContainsPoint checks whether the point passed as argument is contained in the
// rectangle it's called on, returning true when that's the case, false otherwise.
func (r Rect) ContainsPoint(p Point) bool {
	return r.X <= p.X && (r.X+r.W) > p.X && r.Y <= p.Y && (r.Y+r.H) > p.Y
}

// ContainsRect checks whether the rectangle passed as argument is contained in
// the one it's called on, returning true when that's the case, false otherwise.
func (r Rect) ContainsRect(r1 Rect) bool {
	return r.X <= r1.X && (r.X+r.W) >= (r1.X+r1.W) && r.Y <= r1.Y && (r.Y+r.H) >= (r1.Y+r1.H)
}

// The String method returns a human-readable representation of the rectangle.
func (r Rect) String() string {
	return fmt.Sprintf("{ %.6g, %.6g, %.6g, %.6g }", r.X, r.Y, r.W, r.H)
}

// Path satisfies the Shape interface, allowing Rect values to be used with
// programs that manipulate shapes.
func (r Rect) Path() Path {
	return AppendRect(MakePath(5), r)
}

// MergeRect merges a rectangle into an existing list of other rectangles,
// returing the potentially modified slice.
//
// Merging doesn't always append the given rectangle to the list, it won't be
// added if another rectangel in the list already contains the one passed as
// argument for example. A program should consider the list of rectangles passed
// to and returned by the function as the optimal list of rectangle that would
// contain all the merged rectangles. This is particulary useful when keeping
// track of 'dirty' areas in a way that would minimize the 'dirty' surface.
func MergeRect(list []Rect, rect Rect) []Rect {
	// First we check if one of the rectangles in the slice contains the one we
	// are trying to add. If that's the case there's nothing we need to do, the
	// rectangle is already contained in the slice.
	for _, r := range list {
		if r.ContainsRect(rect) {
			return list
		}
	}

	// Look for rectangles that may intersect with the one we are trying to add,
	// if one is found we merge them and rebuild the slice in case it created
	// new intersections.
	for i, r := range list {
		if !r.Intersect(rect).Empty() {
			s := append(make([]Rect, 0, len(list)), r.Merge(rect))

			for j, r := range list {
				if i != j {
					s = MergeRect(s, r)
				}
			}

			return s
		}
	}

	// TOOD: more clever merging policies?
	// -----------------------------------
	//
	// - don't merge if the resulting area is greater than keeping rectangles
	// - look for the merge that creates the smallest area
	// - optimize memory allocation, only recreate the list if needed
	// - sort the list by areas in descending order to improve the fast path
	//
	// While these merges could greatly reduce the areas being redrawn they
	// could also be expansive to compute for potentially little gain due to
	// the way widgets are laid out (parents contain children).
	// It is also possible that generating more redrawing calls may be slower
	// to render than asking the window system to redraw a bigger area once.

	// If none of the merging policies have matched we simply append it to the
	// slice.
	return append(list, rect)
}
<|endoftext|>


<commit_before>// Copyright 2016 CoreOS, Inc.
//
// 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.

// The packet provider fetches a remote configuration from the packet.net
// userdata metadata service URL.

package packet

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"strings"

	"github.com/coreos/ignition/config/validate/report"
	"github.com/coreos/ignition/internal/config/types"
	"github.com/coreos/ignition/internal/providers/util"
	"github.com/coreos/ignition/internal/resource"
)

var (
	ErrValidFetchEmptyData = errors.New("fetch successful but fetched data empty")
)

var (
	userdataUrl = url.URL{
		Scheme: "https",
		Host:   "metadata.packet.net",
		Path:   "userdata",
	}
)

var (
	metadataUrl = url.URL{
		Scheme: "https",
		Host:   "metadata.packet.net",
		Path:   "metadata",
	}
)

func FetchConfig(f resource.Fetcher) (types.Config, report.Report, error) {
	// Packet's metadata service returns "Not Acceptable" when queried
	// with the default Accept header.
	headers := resource.ConfigHeaders
	headers.Set("Accept", "*/*")
	data, err := f.FetchToBuffer(userdataUrl, resource.FetchOptions{
		Headers: headers,
	})
	if err != nil {
		return types.Config{}, report.Report{}, err
	}

	return util.ParseConfig(f.Logger, data)
}

// PostStatus posts a message that will show on the Packet Instance Timeline
func PostStatus(stageName string, f resource.Fetcher, errMsg error) error {
	f.Logger.Info("POST message to Packet Timeline")
	// fetch JSON from https://metadata.packet.net/metadata
	data, err := f.FetchToBuffer(metadataUrl, resource.FetchOptions{
		Headers: nil,
	})
	if err != nil {
		return err
	}
	if data == nil {
		return ErrValidFetchEmptyData
	}
	metadata := struct {
		PhoneHomeURL string `json:"phone_home_url"`
	}{}
	err = json.Unmarshal(data, &metadata)
	if err != nil {
		return err
	}
	phonehomeURL := metadata.PhoneHomeURL
	// to get phonehome IPv4
	phonehomeURL = strings.TrimSuffix(phonehomeURL, "/phone-home")
	// POST Message to phonehome IP
	postMessageURL := phonehomeURL + "/events"

	return postMessage(stageName, errMsg, postMessageURL)
}

// postMessage makes a post request with the supplied message to the url
func postMessage(stageName string, e error, url string) error {

	stageName = "[" + stageName + "]"

	type mStruct struct {
		State   string `json:"state"`
		Message string `json:"message"`
	}
	m := mStruct{}
	if e != nil {
		m = mStruct{
			State:   "failed",
			Message: stageName + " Ignition error: " + e.Error(),
		}
	} else {
		m = mStruct{
			State:   "succeeded",
			Message: stageName + " Ignition status: finished successfully",
		}
	}
	messageJSON, err := json.Marshal(m)
	if err != nil {
		return err
	}
	postReq, err := http.NewRequest("POST", url, bytes.NewBuffer(messageJSON))
	if err != nil {
		return err
	}
	postReq.Header.Set("Content-Type", "application/json")
	client := &http.Client{}
	respPost, err := client.Do(postReq)
	if err != nil {
		return err
	}
	defer respPost.Body.Close()
	return err
}
<commit_msg>internal/providers/packet: don't send success to packet from Ignition<commit_after>// Copyright 2016 CoreOS, Inc.
//
// 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.

// The packet provider fetches a remote configuration from the packet.net
// userdata metadata service URL.

package packet

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"strings"

	"github.com/coreos/ignition/config/validate/report"
	"github.com/coreos/ignition/internal/config/types"
	"github.com/coreos/ignition/internal/providers/util"
	"github.com/coreos/ignition/internal/resource"
)

var (
	ErrValidFetchEmptyData = errors.New("fetch successful but fetched data empty")
)

var (
	userdataUrl = url.URL{
		Scheme: "https",
		Host:   "metadata.packet.net",
		Path:   "userdata",
	}
)

var (
	metadataUrl = url.URL{
		Scheme: "https",
		Host:   "metadata.packet.net",
		Path:   "metadata",
	}
)

func FetchConfig(f resource.Fetcher) (types.Config, report.Report, error) {
	// Packet's metadata service returns "Not Acceptable" when queried
	// with the default Accept header.
	headers := resource.ConfigHeaders
	headers.Set("Accept", "*/*")
	data, err := f.FetchToBuffer(userdataUrl, resource.FetchOptions{
		Headers: headers,
	})
	if err != nil {
		return types.Config{}, report.Report{}, err
	}

	return util.ParseConfig(f.Logger, data)
}

// PostStatus posts a message that will show on the Packet Instance Timeline
func PostStatus(stageName string, f resource.Fetcher, errMsg error) error {
	f.Logger.Info("POST message to Packet Timeline")
	// fetch JSON from https://metadata.packet.net/metadata
	data, err := f.FetchToBuffer(metadataUrl, resource.FetchOptions{
		Headers: nil,
	})
	if err != nil {
		return err
	}
	if data == nil {
		return ErrValidFetchEmptyData
	}
	metadata := struct {
		PhoneHomeURL string `json:"phone_home_url"`
	}{}
	err = json.Unmarshal(data, &metadata)
	if err != nil {
		return err
	}
	phonehomeURL := metadata.PhoneHomeURL
	// to get phonehome IPv4
	phonehomeURL = strings.TrimSuffix(phonehomeURL, "/phone-home")
	// POST Message to phonehome IP
	postMessageURL := phonehomeURL + "/events"

	return postMessage(stageName, errMsg, postMessageURL)
}

// postMessage makes a post request with the supplied message to the url
func postMessage(stageName string, e error, url string) error {

	stageName = "[" + stageName + "]"

	type mStruct struct {
		State   string `json:"state"`
		Message string `json:"message"`
	}
	m := mStruct{}
	if e != nil {
		m = mStruct{
			State:   "failed",
			Message: stageName + " Ignition error: " + e.Error(),
		}
	} else {
		m = mStruct{
			State:   "running",
			Message: stageName + " Ignition status: finished successfully",
		}
	}
	messageJSON, err := json.Marshal(m)
	if err != nil {
		return err
	}
	postReq, err := http.NewRequest("POST", url, bytes.NewBuffer(messageJSON))
	if err != nil {
		return err
	}
	postReq.Header.Set("Content-Type", "application/json")
	client := &http.Client{}
	respPost, err := client.Do(postReq)
	if err != nil {
		return err
	}
	defer respPost.Body.Close()
	return err
}
<|endoftext|>