<commit_before>package transactor

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

	"github.com/chop-dbhi/origins"
	"github.com/chop-dbhi/origins/chrono"
	"github.com/chop-dbhi/origins/storage"
	"github.com/sirupsen/logrus"
)

const (
	macrosDomain = "origins.macros"
)

var (
	ErrCanceled       = errors.New("transactor: canceled")
	ErrNoID           = errors.New("transactor: could not create tx id")
	ErrCommitConflict = errors.New("transactor: commit conflict")
	ErrReceiveTimeout = errors.New("transactor: receive timeout")
	ErrNoDomain       = errors.New("transactor: no fact domain")
	ErrCouldNotRoute  = errors.New("transactor: could not route")
)

// txid increments a global transaction ID.
func txid(s storage.Engine) (uint64, error) {
	return s.Incr("origins", "tx")
}

// Options are used to supply default values as well as alter the behavior
// of a running transaction.
type Options struct {
	// Default domain for unbounded facts. If this is ommitted and a fact
	// does not specify a domain, an error will occur.
	DefaultDomain string

	// Duration of time wait to receive facts before timing out the transaction.
	ReceiveWait time.Duration

	// The router to use for the transaction.
	Router Router

	// Defines the buffer size of the channel that receives facts for processing.
	// Increasing this may increase throughput at the expense of memory.
	BufferSize int
}

// DefaultOptions hold the default options for a transaction.
var DefaultOptions = Options{
	ReceiveWait: time.Minute,
	BufferSize:  1000,
}

// Transaction is the entrypoint for transacting facts.
type Transaction struct {
	// Unique ID for the transaction.
	ID uint64

	// The start and end time of transaction.
	StartTime time.Time
	EndTime   time.Time

	// The error that caused the transaction to fail is one occurs.
	Error error

	Storage storage.Engine

	options Options

	// Router for the transaction.
	router Router

	// Main channel received facts are received by.
	stream chan *origins.Fact

	// Channel that can be passed around for signaling the
	// transaction stream has been closed.
	done chan struct{}

	// Shared error channel for all goroutines to communicate when an
	// error occurs.
	errch chan error

	// Wait groups for main goroutine and pipelines.
	mainwg *sync.WaitGroup
	pipewg *sync.WaitGroup

	// Pipelines and channels.
	pipes map[Pipeline]chan<- *origins.Fact
}

// Stats returns the stats of the transaction which aggregates
// them from the pipelines.
func (tx *Transaction) Stats() []*Stats {
	stats := make([]*Stats, len(tx.pipes))

	var i int

	for pipe := range tx.pipes {
		stats[i] = pipe.Stats()
		i++
	}

	return stats
}

// evaluate evaluates a fact against the log.
func (tx *Transaction) evaluate(f *origins.Fact) error {
	if f.Domain == "" {
		if tx.options.DefaultDomain == "" {
			return ErrNoDomain
		}

		f.Domain = tx.options.DefaultDomain
	}

	// Default to assertion.
	if f.Operation == origins.Noop {
		f.Operation = origins.Assertion
	}

	// Default to fact domain.
	if f.Entity.Domain == "" {
		f.Entity.Domain = f.Domain
	}

	if f.Attribute.Domain == "" {
		f.Attribute.Domain = f.Domain
	}

	// Set the valid time if empty.
	if f.Time == chrono.Zero {
		f.Time = tx.StartTime
	}

	return nil
}

// macro takes a fact and resolves the macro.
func (tx *Transaction) macro(fact *origins.Fact) error {
	// Macro domain. Fact is about the domain itself.
	if fact.Entity.Domain == "origins.macros" {
		switch fact.Entity.Name {
		case "domain":
			fact.Entity.Name = fact.Domain
			fact.Domain = "origins.domains"
			fact.Entity.Domain = ""

		case "tx":
			fact.Domain = fmt.Sprintf("origins.tx.%s", fact.Domain)
			fact.Entity.Domain = ""
			fact.Entity.Name = tx.StartTime.String()

		default:
			return fmt.Errorf("transactor: unknown entity macro: %s", fact.Entity.Name)
		}
	}

	if fact.Value.Domain == "origins.macros" {
		switch fact.Value.Name {
		case "now":
			fact.Value.Domain = ""
			fact.Value.Name = tx.StartTime.String()

		default:
			return fmt.Errorf("transactor: unknown value macro: %s", fact.Value.Name)
		}
	}

	return nil
}

// route uses the router to get the pipeline for the fact.
// Each pipeline
func (tx *Transaction) route(fact *origins.Fact) error {
	var (
		ok   bool
		err  error
		pipe Pipeline
		ch   chan<- *origins.Fact
	)

	// Evaluate.
	if err = tx.evaluate(fact); err != nil {
		return err
	}

	// Convert.
	if err = tx.macro(fact); err != nil {
		return err
	}

	// Route.
	if pipe, err = tx.router.Route(fact); err != nil {
		logrus.Debugf("transactor: could not route: %s", err)
		return ErrCouldNotRoute
	}

	// Get or spawn a pipeline.
	if ch, ok = tx.pipes[pipe]; !ok {
		ch = tx.spawn(pipe)
		tx.pipes[pipe] = ch
	}

	// Send fact to the pipeline.
	ch <- fact

	return nil
}

// receive is the coordinator for receiving and routing facts.
func (tx *Transaction) receive() {
	var (
		err  error
		fact *origins.Fact
	)

	logrus.Debugf("transactor(%d): begin receiving facts", tx.ID)

loop:
	for {
		select {
		// An error occurred in a pipeline.
		case err = <-tx.errch:
			logrus.Debugf("transactor(%d): %s", tx.ID, err)
			close(tx.stream)
			break loop

		// Receive facts from stream and route to pipeline.
		// If an error occurs while routing, stop processing.
		case fact = <-tx.stream:
			if fact == nil {
				logrus.Debugf("transactor(%d): end of stream", tx.ID)
				break loop
			}

			if err = tx.route(fact); err != nil {
				logrus.Debugf("transactor(%d): error routing fact", tx.ID)
				break loop
			}

		// Transaction timeout.
		case <-time.After(tx.options.ReceiveWait):
			logrus.Debugf("transactor(%d): receive timeout", tx.ID)
			err = ErrReceiveTimeout
			break loop
		}
	}

	// Set the error if one occurred.
	tx.Error = err

	// Signal the transaction is closed.
	close(tx.done)

	// Wait for the pipelines to finish there work.
	tx.pipewg.Wait()

	// Mark the end time of processing.
	tx.EndTime = time.Now().UTC()

	// Complete the transaction by committing or aborting.
	tx.complete()

	tx.mainwg.Done()
}

// complete commits or aborts the transaction depending if an error occurred
// during processing.
func (tx *Transaction) complete() {
	var err error

	// No error in the transaction, commit the transaction.
	if tx.Error == nil {
		if err = tx.commit(); err != nil {
			logrus.Errorf("transactor(%d): commit failed: %s", tx.ID, err)
		} else {
			logrus.Debugf("transactor(%d): commit succeeded", tx.ID)
		}
	}

	// Error occurred in the transaction or during the commit. Attempt to abort.
	// TODO: if the abort fails, how can the storage garbage be reclaimed?
	if tx.Error != nil || err != nil {
		if err = tx.abort(); err != nil {
			logrus.Errorf("transactor(%d): abort failed: %s", tx.ID, err)
		} else {
			logrus.Debugf("transactor(%d): abort succeeded", tx.ID)
		}
	}
}

// commit commits all the pipelines in a transaction.
func (tx *Transaction) commit() error {
	return tx.Storage.Multi(func(etx storage.Tx) error {
		for pipe, _ := range tx.pipes {
			if err := pipe.Commit(etx); err != nil {
				return err
			}

			logrus.Debugf("transactor(%d): committed pipeline %v", tx.ID, pipe)
		}

		return nil
	})
}

// abort aborts all of the pipelines in a transaction.
func (tx *Transaction) abort() error {
	return tx.Storage.Multi(func(etx storage.Tx) error {
		for pipe, _ := range tx.pipes {
			if err := pipe.Abort(etx); err != nil {
				return err
			}

			logrus.Debugf("transactor(%d): aborted pipeline %v", tx.ID, pipe)
		}

		return nil
	})
}

// spawn all values on the channel on the pipeline.
func (tx *Transaction) spawn(pipe Pipeline) chan<- *origins.Fact {
	ch := make(chan *origins.Fact)

	tx.pipes[pipe] = ch
	tx.pipewg.Add(1)

	go func() {
		defer func() {
			close(ch)
			tx.pipewg.Done()
		}()

		var (
			err  error
			fact *origins.Fact
		)

		// Initialize the pipeline. If an error occurs, send it to the transaction's error channel
		// which will trigger the cancellation procedure.
		if err = pipe.Init(tx); err != nil {
			tx.errch <- err
			return
		}

		logrus.Debugf("transactor(%d): initialized pipeline %T(%s)", tx.ID, pipe, pipe)

		// Reads facts from the channel until there are no more or break
		// if an error occurs from the pipeline.
		for {
			select {
			case <-tx.done:
				return

			case fact = <-ch:
				if err = pipe.Receive(fact); err != nil {
					tx.errch <- err
					return
				}
			}
		}
	}()

	return ch
}

func (tx *Transaction) Cancel() error {
	tx.errch <- ErrCanceled
	tx.mainwg.Wait()
	return tx.Error
}

func (tx *Transaction) Commit() error {
	close(tx.stream)
	tx.mainwg.Wait()
	return tx.Error
}

func (tx *Transaction) Append(fact *origins.Fact) error {
	tx.stream <- fact
	return nil
}

func (tx *Transaction) Consume(pub origins.Publisher) error {
	var (
		ok   bool
		err  error
		fact *origins.Fact
	)

	// Subscribe to the publisher. It takes a channel to signal when
	// this consumer is done and returns a channel that produces facts.
	ch, errch := pub.Subscribe(tx.done)

	// Consume facts until the producer is closed. This may occur upstream
	// by the producer itself or the transaction is closed prematurely.
	for {
		select {
		case err = <-errch:
			return err

		case fact, ok = <-ch:
			// Publisher closed the channel.
			if !ok {
				return nil
			}

			tx.stream <- fact
		}
	}

	return nil
}

func (tx *Transaction) AppendIter(iter origins.Iterator) error {
	var (
		err  error
		fact *origins.Fact
	)

	for {
		fact, err = iter.Next()

		if err != nil {
			return err
		}

		if fact == nil {
			break
		}

		tx.stream <- fact
	}

	return nil
}

// New initializes and returns a transaction for passed storage engine. The options
// are used to change the behavior of the transaction itself.
func New(engine storage.Engine, options Options) (*Transaction, error) {
	// Get the next transaction ID.
	id, err := txid(engine)

	if err != nil {
		logrus.Errorf("transactor: could not generate ID: %s", err)
		return nil, ErrNoID
	}

	if options.Router == nil {
		options.Router = NewDomainRouter()
	}

	if options.ReceiveWait == 0 {
		options.ReceiveWait = DefaultOptions.ReceiveWait
	}

	if options.BufferSize == 0 {
		options.BufferSize = DefaultOptions.BufferSize
	}

	tx := Transaction{
		ID:        id,
		StartTime: time.Now().UTC(),
		Storage:   engine,
		options:   options,
		pipes:     make(map[Pipeline]chan<- *origins.Fact),
		router:    options.Router,
		stream:    make(chan *origins.Fact, options.BufferSize),
		done:      make(chan struct{}),
		errch:     make(chan error),
		pipewg:    &sync.WaitGroup{},
		mainwg:    &sync.WaitGroup{},
	}

	tx.mainwg.Add(1)

	// Start the receiving goroutine.
	go tx.receive()

	return &tx, nil
}
<commit_msg>Fix bad comparison between zero times<commit_after>package transactor

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

	"github.com/chop-dbhi/origins"
	"github.com/chop-dbhi/origins/storage"
	"github.com/sirupsen/logrus"
)

const (
	macrosDomain = "origins.macros"
)

var (
	ErrCanceled       = errors.New("transactor: canceled")
	ErrNoID           = errors.New("transactor: could not create tx id")
	ErrCommitConflict = errors.New("transactor: commit conflict")
	ErrReceiveTimeout = errors.New("transactor: receive timeout")
	ErrNoDomain       = errors.New("transactor: no fact domain")
	ErrCouldNotRoute  = errors.New("transactor: could not route")
)

// txid increments a global transaction ID.
func txid(s storage.Engine) (uint64, error) {
	return s.Incr("origins", "tx")
}

// Options are used to supply default values as well as alter the behavior
// of a running transaction.
type Options struct {
	// Default domain for unbounded facts. If this is ommitted and a fact
	// does not specify a domain, an error will occur.
	DefaultDomain string

	// Duration of time wait to receive facts before timing out the transaction.
	ReceiveWait time.Duration

	// The router to use for the transaction.
	Router Router

	// Defines the buffer size of the channel that receives facts for processing.
	// Increasing this may increase throughput at the expense of memory.
	BufferSize int
}

// DefaultOptions hold the default options for a transaction.
var DefaultOptions = Options{
	ReceiveWait: time.Minute,
	BufferSize:  1000,
}

// Transaction is the entrypoint for transacting facts.
type Transaction struct {
	// Unique ID for the transaction.
	ID uint64

	// The start and end time of transaction.
	StartTime time.Time
	EndTime   time.Time

	// The error that caused the transaction to fail is one occurs.
	Error error

	Storage storage.Engine

	options Options

	// Router for the transaction.
	router Router

	// Main channel received facts are received by.
	stream chan *origins.Fact

	// Channel that can be passed around for signaling the
	// transaction stream has been closed.
	done chan struct{}

	// Shared error channel for all goroutines to communicate when an
	// error occurs.
	errch chan error

	// Wait groups for main goroutine and pipelines.
	mainwg *sync.WaitGroup
	pipewg *sync.WaitGroup

	// Pipelines and channels.
	pipes map[Pipeline]chan<- *origins.Fact
}

// Stats returns the stats of the transaction which aggregates
// them from the pipelines.
func (tx *Transaction) Stats() []*Stats {
	stats := make([]*Stats, len(tx.pipes))

	var i int

	for pipe := range tx.pipes {
		stats[i] = pipe.Stats()
		i++
	}

	return stats
}

// evaluate evaluates a fact against the log.
func (tx *Transaction) evaluate(f *origins.Fact) error {
	if f.Domain == "" {
		if tx.options.DefaultDomain == "" {
			return ErrNoDomain
		}

		f.Domain = tx.options.DefaultDomain
	}

	// Default to assertion.
	if f.Operation == origins.Noop {
		f.Operation = origins.Assertion
	}

	// Default to fact domain.
	if f.Entity.Domain == "" {
		f.Entity.Domain = f.Domain
	}

	if f.Attribute.Domain == "" {
		f.Attribute.Domain = f.Domain
	}

	// Set the valid time if empty.
	if f.Time.IsZero() {
		f.Time = tx.StartTime
	}

	return nil
}

// macro takes a fact and resolves the macro.
func (tx *Transaction) macro(fact *origins.Fact) error {
	// Macro domain. Fact is about the domain itself.
	if fact.Entity.Domain == "origins.macros" {
		switch fact.Entity.Name {
		case "domain":
			fact.Entity.Name = fact.Domain
			fact.Domain = "origins.domains"
			fact.Entity.Domain = ""

		case "tx":
			fact.Domain = fmt.Sprintf("origins.tx.%s", fact.Domain)
			fact.Entity.Domain = ""
			fact.Entity.Name = tx.StartTime.String()

		default:
			return fmt.Errorf("transactor: unknown entity macro: %s", fact.Entity.Name)
		}
	}

	if fact.Value.Domain == "origins.macros" {
		switch fact.Value.Name {
		case "now":
			fact.Value.Domain = ""
			fact.Value.Name = tx.StartTime.String()

		default:
			return fmt.Errorf("transactor: unknown value macro: %s", fact.Value.Name)
		}
	}

	return nil
}

// route uses the router to get the pipeline for the fact.
// Each pipeline
func (tx *Transaction) route(fact *origins.Fact) error {
	var (
		ok   bool
		err  error
		pipe Pipeline
		ch   chan<- *origins.Fact
	)

	// Evaluate.
	if err = tx.evaluate(fact); err != nil {
		return err
	}

	// Convert.
	if err = tx.macro(fact); err != nil {
		return err
	}

	// Route.
	if pipe, err = tx.router.Route(fact); err != nil {
		logrus.Debugf("transactor: could not route: %s", err)
		return ErrCouldNotRoute
	}

	// Get or spawn a pipeline.
	if ch, ok = tx.pipes[pipe]; !ok {
		ch = tx.spawn(pipe)
		tx.pipes[pipe] = ch
	}

	// Send fact to the pipeline.
	ch <- fact

	return nil
}

// receive is the coordinator for receiving and routing facts.
func (tx *Transaction) receive() {
	var (
		err  error
		fact *origins.Fact
	)

	logrus.Debugf("transactor(%d): begin receiving facts", tx.ID)

loop:
	for {
		select {
		// An error occurred in a pipeline.
		case err = <-tx.errch:
			logrus.Debugf("transactor(%d): %s", tx.ID, err)
			close(tx.stream)
			break loop

		// Receive facts from stream and route to pipeline.
		// If an error occurs while routing, stop processing.
		case fact = <-tx.stream:
			if fact == nil {
				logrus.Debugf("transactor(%d): end of stream", tx.ID)
				break loop
			}

			if err = tx.route(fact); err != nil {
				logrus.Debugf("transactor(%d): error routing fact", tx.ID)
				break loop
			}

		// Transaction timeout.
		case <-time.After(tx.options.ReceiveWait):
			logrus.Debugf("transactor(%d): receive timeout", tx.ID)
			err = ErrReceiveTimeout
			break loop
		}
	}

	// Set the error if one occurred.
	tx.Error = err

	// Signal the transaction is closed.
	close(tx.done)

	// Wait for the pipelines to finish there work.
	tx.pipewg.Wait()

	// Mark the end time of processing.
	tx.EndTime = time.Now().UTC()

	// Complete the transaction by committing or aborting.
	tx.complete()

	tx.mainwg.Done()
}

// complete commits or aborts the transaction depending if an error occurred
// during processing.
func (tx *Transaction) complete() {
	var err error

	// No error in the transaction, commit the transaction.
	if tx.Error == nil {
		if err = tx.commit(); err != nil {
			logrus.Errorf("transactor(%d): commit failed: %s", tx.ID, err)
		} else {
			logrus.Debugf("transactor(%d): commit succeeded", tx.ID)
		}
	}

	// Error occurred in the transaction or during the commit. Attempt to abort.
	// TODO: if the abort fails, how can the storage garbage be reclaimed?
	if tx.Error != nil || err != nil {
		if err = tx.abort(); err != nil {
			logrus.Errorf("transactor(%d): abort failed: %s", tx.ID, err)
		} else {
			logrus.Debugf("transactor(%d): abort succeeded", tx.ID)
		}
	}
}

// commit commits all the pipelines in a transaction.
func (tx *Transaction) commit() error {
	return tx.Storage.Multi(func(etx storage.Tx) error {
		for pipe, _ := range tx.pipes {
			if err := pipe.Commit(etx); err != nil {
				return err
			}

			logrus.Debugf("transactor(%d): committed pipeline %v", tx.ID, pipe)
		}

		return nil
	})
}

// abort aborts all of the pipelines in a transaction.
func (tx *Transaction) abort() error {
	return tx.Storage.Multi(func(etx storage.Tx) error {
		for pipe, _ := range tx.pipes {
			if err := pipe.Abort(etx); err != nil {
				return err
			}

			logrus.Debugf("transactor(%d): aborted pipeline %v", tx.ID, pipe)
		}

		return nil
	})
}

// spawn all values on the channel on the pipeline.
func (tx *Transaction) spawn(pipe Pipeline) chan<- *origins.Fact {
	ch := make(chan *origins.Fact)

	tx.pipes[pipe] = ch
	tx.pipewg.Add(1)

	go func() {
		defer func() {
			close(ch)
			tx.pipewg.Done()
		}()

		var (
			err  error
			fact *origins.Fact
		)

		// Initialize the pipeline. If an error occurs, send it to the transaction's error channel
		// which will trigger the cancellation procedure.
		if err = pipe.Init(tx); err != nil {
			tx.errch <- err
			return
		}

		logrus.Debugf("transactor(%d): initialized pipeline %T(%s)", tx.ID, pipe, pipe)

		// Reads facts from the channel until there are no more or break
		// if an error occurs from the pipeline.
		for {
			select {
			case <-tx.done:
				return

			case fact = <-ch:
				if err = pipe.Receive(fact); err != nil {
					tx.errch <- err
					return
				}
			}
		}
	}()

	return ch
}

func (tx *Transaction) Cancel() error {
	tx.errch <- ErrCanceled
	tx.mainwg.Wait()
	return tx.Error
}

func (tx *Transaction) Commit() error {
	close(tx.stream)
	tx.mainwg.Wait()
	return tx.Error
}

func (tx *Transaction) Append(fact *origins.Fact) error {
	tx.stream <- fact
	return nil
}

func (tx *Transaction) Consume(pub origins.Publisher) error {
	var (
		ok   bool
		err  error
		fact *origins.Fact
	)

	// Subscribe to the publisher. It takes a channel to signal when
	// this consumer is done and returns a channel that produces facts.
	ch, errch := pub.Subscribe(tx.done)

	// Consume facts until the producer is closed. This may occur upstream
	// by the producer itself or the transaction is closed prematurely.
	for {
		select {
		case err = <-errch:
			return err

		case fact, ok = <-ch:
			// Publisher closed the channel.
			if !ok {
				return nil
			}

			tx.stream <- fact
		}
	}

	return nil
}

func (tx *Transaction) AppendIter(iter origins.Iterator) error {
	var (
		err  error
		fact *origins.Fact
	)

	for {
		fact, err = iter.Next()

		if err != nil {
			return err
		}

		if fact == nil {
			break
		}

		tx.stream <- fact
	}

	return nil
}

// New initializes and returns a transaction for passed storage engine. The options
// are used to change the behavior of the transaction itself.
func New(engine storage.Engine, options Options) (*Transaction, error) {
	// Get the next transaction ID.
	id, err := txid(engine)

	if err != nil {
		logrus.Errorf("transactor: could not generate ID: %s", err)
		return nil, ErrNoID
	}

	if options.Router == nil {
		options.Router = NewDomainRouter()
	}

	if options.ReceiveWait == 0 {
		options.ReceiveWait = DefaultOptions.ReceiveWait
	}

	if options.BufferSize == 0 {
		options.BufferSize = DefaultOptions.BufferSize
	}

	tx := Transaction{
		ID:        id,
		StartTime: time.Now().UTC(),
		Storage:   engine,
		options:   options,
		pipes:     make(map[Pipeline]chan<- *origins.Fact),
		router:    options.Router,
		stream:    make(chan *origins.Fact, options.BufferSize),
		done:      make(chan struct{}),
		errch:     make(chan error),
		pipewg:    &sync.WaitGroup{},
		mainwg:    &sync.WaitGroup{},
	}

	tx.mainwg.Add(1)

	// Start the receiving goroutine.
	go tx.receive()

	return &tx, nil
}
<|endoftext|>


<commit_before>package model

import (
	"github.com/fuxiaohei/purine/log"
	"github.com/fuxiaohei/purine/vars"
	"net/url"
	"time"
)

const (
	ARTICLE_STATUS_PUBLISH = "publish"
	ARTICLE_STATUS_DRAFT   = "draft"
	ARTICLE_STATUS_DELETE  = "deleted"

	ARTICLE_COMMENT_OPEN  = "open"
	ARTICLE_COMMENT_CLOSE = "closed"
	ARTICLE_COMMENT_WAIT  = "wait" // waiting 30 days to close
)

type Article struct {
	Id       int64
	AuthorId int64

	Title      string
	CreateTime int64  `xorm:"created"`
	UpdateTime int64  `xorm:"updated"`
	Link       string `xorm:"unique"`
	Preview    string
	Body       string
	Topic      string
	TagString  string

	Hits          int64
	Comments      int64
	Status        string
	CommentStatus string

	tagData []*Tag `xorm:"-"`
}

func (a *Article) Date(layout string) string {
	return time.Unix(a.CreateTime, 0).Format(layout)
}

func (a *Article) Href() string {
	return url.QueryEscape(a.Title)
}

func (a *Article) IsDraft() bool {
	return a.Status == ARTICLE_STATUS_DRAFT
}

func ListGeneralArticle(page, size int64, order string) ([]*Article, error) {
	articles := make([]*Article, 0)
	if err := vars.Db.Where("status != ?", ARTICLE_STATUS_DELETE).
		Limit(int(page), int((page-1)*size)).OrderBy(order).Find(&articles); err != nil {
		log.Error("Db|ListGeneralArticle|%d,%d|%s|%s", page, size, order, err.Error())
		return nil, err
	}
	return articles, nil
}

func CountGeneralArticle() (int64, error) {
	return vars.Db.Where("status != ?", ARTICLE_STATUS_DELETE).Count(new(Article))
}

func ListStatusArticle(status string, page, size int64, order string) ([]*Article, error) {
	articles := make([]*Article, 0)
	if err := vars.Db.Where("status = ?", status).
		Limit(int(page), int((page-1)*size)).OrderBy(order).Find(&articles); err != nil {
		log.Error("Db|ListStatusArticle|%s|%d,%d|%s|%s", status, page, size, order, err.Error())
		return nil, err
	}
	return articles, nil
}

func CountStatusArticle(status string) (int64, error) {
	return vars.Db.Where("status = ?", status).Count(new(Article))
}

func GetArticleBy(col string, v interface{}) (*Article, error) {
	a := new(Article)
	if _, err := vars.Db.Where(col+" = ?", v).Get(a); err != nil {
		log.Error("Db|GetArticleBy|%s,%v|%s", col, v, err.Error())
		return nil, err
	}
	if a.Id > 0 {
		return a, nil
	}
	return nil, nil
}

func SaveArticle(a *Article) (*Article, error) {
	if a.Id > 0 {
		if _, err := vars.Db.Where("id = ?", a.Id).
			Cols("title,link,update_time,preview,body,topic,tag_string,status,comment_status").
			Update(a); err != nil {
			log.Error("Db|SaveArticle|%d|%s", a.Id, err.Error())
			return nil, err
		}
	} else {
		if _, err := vars.Db.Insert(a); err != nil {
			log.Error("Db|SaveArticle|%d|%s", a.Id, err.Error())
			return nil, err
		}
	}
	return GetArticleBy("id", a.Id)
}

type Tag struct {
	Id        int64
	ArticleId int64
	Tag       string
}
<commit_msg>save tags action<commit_after>package model

import (
	"github.com/fuxiaohei/purine/log"
	"github.com/fuxiaohei/purine/vars"
	"net/url"
	"strings"
	"time"
)

const (
	ARTICLE_STATUS_PUBLISH = "publish"
	ARTICLE_STATUS_DRAFT   = "draft"
	ARTICLE_STATUS_DELETE  = "deleted"

	ARTICLE_COMMENT_OPEN  = "open"
	ARTICLE_COMMENT_CLOSE = "closed"
	ARTICLE_COMMENT_WAIT  = "wait" // waiting 30 days to close
)

type Article struct {
	Id       int64
	AuthorId int64

	Title      string
	CreateTime int64  `xorm:"created"`
	UpdateTime int64  `xorm:"updated"`
	Link       string `xorm:"unique"`
	Preview    string
	Body       string
	Topic      string
	TagString  string

	Hits          int64
	Comments      int64
	Status        string
	CommentStatus string

	tagData []*Tag `xorm:"-"`
}

func (a *Article) Date(layout string) string {
	return time.Unix(a.CreateTime, 0).Format(layout)
}

func (a *Article) Href() string {
	return url.QueryEscape(a.Title)
}

func (a *Article) IsDraft() bool {
	return a.Status == ARTICLE_STATUS_DRAFT
}

func ListGeneralArticle(page, size int64, order string) ([]*Article, error) {
	articles := make([]*Article, 0)
	if err := vars.Db.Where("status != ?", ARTICLE_STATUS_DELETE).
		Limit(int(page), int((page-1)*size)).OrderBy(order).Find(&articles); err != nil {
		log.Error("Db|ListGeneralArticle|%d,%d|%s|%s", page, size, order, err.Error())
		return nil, err
	}
	return articles, nil
}

func CountGeneralArticle() (int64, error) {
	return vars.Db.Where("status != ?", ARTICLE_STATUS_DELETE).Count(new(Article))
}

func ListStatusArticle(status string, page, size int64, order string) ([]*Article, error) {
	articles := make([]*Article, 0)
	if err := vars.Db.Where("status = ?", status).
		Limit(int(page), int((page-1)*size)).OrderBy(order).Find(&articles); err != nil {
		log.Error("Db|ListStatusArticle|%s|%d,%d|%s|%s", status, page, size, order, err.Error())
		return nil, err
	}
	return articles, nil
}

func CountStatusArticle(status string) (int64, error) {
	return vars.Db.Where("status = ?", status).Count(new(Article))
}

func GetArticleBy(col string, v interface{}) (*Article, error) {
	a := new(Article)
	if _, err := vars.Db.Where(col+" = ?", v).Get(a); err != nil {
		log.Error("Db|GetArticleBy|%s,%v|%s", col, v, err.Error())
		return nil, err
	}
	if a.Id > 0 {
		return a, nil
	}
	return nil, nil
}

func SaveArticle(a *Article) (*Article, error) {
	if a.Id > 0 {
		if _, err := vars.Db.Where("id = ?", a.Id).
			Cols("title,link,update_time,preview,body,topic,tag_string,status,comment_status").
			Update(a); err != nil {
			log.Error("Db|SaveArticle|%d|%s", a.Id, err.Error())
			return nil, err
		}
	} else {
		if _, err := vars.Db.Insert(a); err != nil {
			log.Error("Db|SaveArticle|%d|%s", a.Id, err.Error())
			return nil, err
		}

	}
	if a.TagString != "" {
		if err := saveTags(a.Id, a.TagString); err != nil {
			return nil, err
		}
	}
	return GetArticleBy("id", a.Id)
}

func saveTags(id int64, tagStr string) error {
	// delete old tags
	if _, err := vars.Db.Where("article_id = ?", id).Delete(new(Tag)); err != nil {
		log.Error("Db|SaveTags|%d,%s|%s", id, tagStr, err.Error())
		return err
	}
	// save new tags
	tags := strings.Split(strings.Replace(tagStr, "，", ",", -1), ",")
	for _, t := range tags {
		if _, err := vars.Db.Insert(&Tag{ArticleId: id, Tag: t}); err != nil {
			log.Error("Db|SaveTags|%d,%s|%s", id, t, err.Error())
			return err
		}
	}
	return nil
}

type Tag struct {
	Id        int64
	ArticleId int64
	Tag       string
}
<|endoftext|>


<commit_before>package context

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"
)

type Context struct {
	path string
}

func NewContext(path string) (*Context, error) {
	context := &Context{
		path: path,
	}

	return context, nil
}

func (c *Context) FilesToBeCommitted() ([]string, error) {
	cmd := exec.Command("git", "diff", "--name-only", "--cached")

	outputBytes, err := cmd.CombinedOutput()
	if err != nil { return nil, err }

	output := string(outputBytes)
	lines := strings.Split(strings.TrimSpace(output), "\n")

	// Verify that all the lines are *actually* files
	for _, line := range lines {
		stat, err := os.Stat(line)
		if err != nil { return nil, err }

		if stat.IsDir() {
			return nil, fmt.Errorf("Unexpected directory in list of staged files: %v", line)
		}
	}

	return lines, nil
}

func (c *Context) ExecutablesForHook(hook string) ([]string, error) {
	shortPath    := path.Join(".quickhook", hook)
	absolutePath := path.Join(c.path, shortPath)

	allFiles, err := ioutil.ReadDir(absolutePath)
	if err != nil { return nil, err }

	var executables []string
	for _, fileInfo := range allFiles {
		if fileInfo.IsDir() { continue }

		name := fileInfo.Name()

		if (fileInfo.Mode() & 0111) > 0 {
			executables = append(executables, path.Join(shortPath, name))
		} else {
			fmt.Printf("Warning: Non-executable file found in %v: %v\n", shortPath, name)
		}
	}

	return executables, nil
}
<commit_msg>Improve processing of data from listing staged files in Git<commit_after>package context

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"
)

type Context struct {
	path string
}

func NewContext(path string) (*Context, error) {
	context := &Context{
		path: path,
	}

	return context, nil
}

func (c *Context) FilesToBeCommitted() ([]string, error) {
	cmd := exec.Command("git", "diff", "--name-only", "--cached")

	outputBytes, err := cmd.CombinedOutput()
	if err != nil { return nil, err }

	output := string(outputBytes)
	lines := strings.Split(output, "\n")

	var files []string

	// Verify that all the lines are *actually* files
	for _, line := range lines {
		file := strings.TrimSpace(line)
		if len(file) == 0 { continue }

		stat, err := os.Stat(file)
		if err != nil { return nil, err }

		if stat.IsDir() {
			return nil, fmt.Errorf("Unexpected directory in list of staged files: %v", file)
		}

		files = append(files, file)
	}

	return files, nil
}

func (c *Context) ExecutablesForHook(hook string) ([]string, error) {
	shortPath    := path.Join(".quickhook", hook)
	absolutePath := path.Join(c.path, shortPath)

	allFiles, err := ioutil.ReadDir(absolutePath)
	if err != nil { return nil, err }

	var executables []string
	for _, fileInfo := range allFiles {
		if fileInfo.IsDir() { continue }

		name := fileInfo.Name()

		if (fileInfo.Mode() & 0111) > 0 {
			executables = append(executables, path.Join(shortPath, name))
		} else {
			fmt.Printf("Warning: Non-executable file found in %v: %v\n", shortPath, name)
		}
	}

	return executables, nil
}
<|endoftext|>


<commit_before>package model

import (
	"io/ioutil"
	"os"

	"github.com/jakubknejzlik/kontena-git-cli/utils"

	yaml2 "gopkg.in/yaml.v2"
)

// KontenaSecret ...
type KontenaSecret struct {
	Secret string `yaml:"secret,omitempty"`
	Name   string `yaml:"name,omitempty"`
	Type   string `yaml:"type,omitempty"`
}

// KontenaDeploy ...
type KontenaDeploy struct {
	Strategy string `yaml:"strategy,omitempty"`
}

// KontenaService ...
type KontenaService struct {
	ContainerName string `yaml:"container_name,omitempty"`
	Instances     *int   `yaml:"instances,omitempty"`
	Image         string `yaml:"image,omitempty"`
	Command       string `yaml:"command,omitempty"`
	// Entrypoint    string            `yaml:"entrypoint,omitempty"`
	Volumes     []string          `yaml:"volumes,omitempty"`
	Labels      map[string]string `yaml:"labels,omitempty"`
	Environment []string          `yaml:"environment,omitempty"`
	Links       []string          `yaml:"links,omitempty"`
	Ports       []string          `yaml:"ports,omitempty"`
	Secrets     []KontenaSecret   `yaml:"secrets,omitempty"`
	Deploy      KontenaDeploy     `yaml:"deploy,omitempty"`
}

// KontenaStack ...
type KontenaStack struct {
	Name     string                    `yaml:"stack,omitempty"`
	Version  string                    `yaml:"version,omitempty"`
	Services map[string]KontenaService `yaml:"services,omitempty"`
}

// KontenaLoad ...
func KontenaLoad(path string) (KontenaStack, error) {
	var dc KontenaStack
	data, err := ioutil.ReadFile(path)

	if err != nil {
		return dc, err
	}
	yaml2.Unmarshal(data, &dc)
	return dc, nil
}

// ExportTemporary ...
func (c KontenaStack) ExportTemporary(translateSecrets bool) (string, error) {
	var path string
	tmp, err := ioutil.TempFile(os.TempDir(), "kontena")
	if err != nil {
		return path, err
	}

	path = tmp.Name()
	stack := c

	if translateSecrets {
		newServices := map[string]KontenaService{}
		for i, service := range stack.Services {
			newSecrets := []KontenaSecret{}
			for _, secret := range service.Secrets {
				newSecrets = append(newSecrets, KontenaSecret{
					Secret: stack.Name + "_" + secret.Secret,
					Name:   secret.Name,
					Type:   secret.Type,
				})
			}
			service.Secrets = newSecrets
			newServices[i] = service
		}
		stack.Services = newServices
	}

	data, marshalError := yaml2.Marshal(stack)
	if marshalError != nil {
		return path, marshalError
	}

	utils.LogSection("exported kontena file", string(data))
	if err := ioutil.WriteFile(path, data, 0644); err != nil {
		return path, err
	}

	return path, nil
}
<commit_msg>Remove ContainerName from service<commit_after>package model

import (
	"io/ioutil"
	"os"

	"github.com/jakubknejzlik/kontena-git-cli/utils"

	yaml2 "gopkg.in/yaml.v2"
)

// KontenaSecret ...
type KontenaSecret struct {
	Secret string `yaml:"secret,omitempty"`
	Name   string `yaml:"name,omitempty"`
	Type   string `yaml:"type,omitempty"`
}

// KontenaDeploy ...
type KontenaDeploy struct {
	Strategy string `yaml:"strategy,omitempty"`
}

// KontenaService ...
type KontenaService struct {
	Instances *int   `yaml:"instances,omitempty"`
	Image     string `yaml:"image,omitempty"`
	Command   string `yaml:"command,omitempty"`
	// Entrypoint    string            `yaml:"entrypoint,omitempty"`
	Volumes     []string          `yaml:"volumes,omitempty"`
	Labels      map[string]string `yaml:"labels,omitempty"`
	Environment []string          `yaml:"environment,omitempty"`
	Links       []string          `yaml:"links,omitempty"`
	Ports       []string          `yaml:"ports,omitempty"`
	Secrets     []KontenaSecret   `yaml:"secrets,omitempty"`
	Deploy      KontenaDeploy     `yaml:"deploy,omitempty"`
}

// KontenaStack ...
type KontenaStack struct {
	Name     string                    `yaml:"stack,omitempty"`
	Version  string                    `yaml:"version,omitempty"`
	Services map[string]KontenaService `yaml:"services,omitempty"`
}

// KontenaLoad ...
func KontenaLoad(path string) (KontenaStack, error) {
	var dc KontenaStack
	data, err := ioutil.ReadFile(path)

	if err != nil {
		return dc, err
	}
	yaml2.Unmarshal(data, &dc)
	return dc, nil
}

// ExportTemporary ...
func (c KontenaStack) ExportTemporary(translateSecrets bool) (string, error) {
	var path string
	tmp, err := ioutil.TempFile(os.TempDir(), "kontena")
	if err != nil {
		return path, err
	}

	path = tmp.Name()
	stack := c

	if translateSecrets {
		newServices := map[string]KontenaService{}
		for i, service := range stack.Services {
			newSecrets := []KontenaSecret{}
			for _, secret := range service.Secrets {
				newSecrets = append(newSecrets, KontenaSecret{
					Secret: stack.Name + "_" + secret.Secret,
					Name:   secret.Name,
					Type:   secret.Type,
				})
			}
			service.Secrets = newSecrets
			newServices[i] = service
		}
		stack.Services = newServices
	}

	data, marshalError := yaml2.Marshal(stack)
	if marshalError != nil {
		return path, marshalError
	}

	utils.LogSection("exported kontena file", string(data))
	if err := ioutil.WriteFile(path, data, 0644); err != nil {
		return path, err
	}

	return path, nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"syscall"

	"code.google.com/p/go-uuid/uuid"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/service/ec2"
)

type ebsVolumeDriver struct {
	ec2                 *ec2.EC2
	ec2meta             *ec2metadata.Client
	awsInstanceId       string
	awsRegion           string
	awsAvailabilityZone string
	volumes             map[string]string
}

func NewEbsVolumeDriver() (VolumeDriver, error) {
	ec2meta := ec2metadata.New(nil)

	if !ec2meta.Available() {
		return nil, errors.New("Not running on an EC2 instance.")
	}

	d := &ebsVolumeDriver{}

	var err error
	d.awsInstanceId, err = ec2meta.GetMetadata("instance-id")
	if err != nil {
		return nil, err
	}

	d.awsRegion, err = ec2meta.Region()
	if err != nil {
		return nil, err
	}

	d.awsAvailabilityZone, err = ec2meta.GetMetadata("placement/availability-zone")
	if err != nil {
		return nil, err
	}

	return &ebsVolumeDriver{
		ec2:     ec2.New(aws.NewConfig().WithRegion(d.awsRegion)),
		ec2meta: ec2meta,
		volumes: make(map[string]string),
	}, nil
}

func (d *ebsVolumeDriver) getEbsInfo(name string) error {
	// Query EC2 to make sure this volume is indeed available.
	volumes, err := d.ec2.DescribeVolumes(&ec2.DescribeVolumesInput{
		VolumeIds: []*string{
			aws.String(name),
		},
	})
	if err != nil {
		return err
	}
	if len(volumes.Volumes) != 1 {
		return errors.New("Cannot find EBS volume.")
	}

	// TODO: check that it's in the right region.

	return nil
}

func (d *ebsVolumeDriver) Create(name string) error {
	m, exists := d.volumes[name]
	if exists {
		// Docker won't always cleanly remove entries.  It's okay so long
		// as the target isn't already mounted by someone else.
		if m != "" {
			return errors.New("Name already in use.")
		}
	}

	d.volumes[name] = ""
	return nil
}

func (d *ebsVolumeDriver) Mount(name string) (string, error) {
	m, exists := d.volumes[name]
	if !exists {
		return "", errors.New("Name not found.")
	}

	if m != "" {
		return "", errors.New("Volume already mounted.")
	}

	return d.doMount(name)
}

func (d *ebsVolumeDriver) Path(name string) (string, error) {
	m, exists := d.volumes[name]
	if !exists {
		return "", errors.New("Name not found.")
	}

	if m == "" {
		return "", errors.New("Volume not mounted.")
	}

	return m, nil
}

func (d *ebsVolumeDriver) Remove(name string) error {
	m, exists := d.volumes[name]
	if !exists {
		return errors.New("Name not found.")
	}

	// If the volume is still mounted, unmount it before removing it.
	if m != "" {
		err := d.doUnmount(name)
		if err != nil {
			return err
		}
	}

	delete(d.volumes, name)
	return nil
}

func (d *ebsVolumeDriver) Unmount(name string) error {
	m, exists := d.volumes[name]
	if !exists {
		return errors.New("Name not found.")
	}

	// If the volume is mounted, go ahead and unmount it.  Ignore requests
	// to unmount volumes that aren't actually mounted.
	if m != "" {
		err := d.doUnmount(name)
		if err != nil {
			return err
		}
	}

	return nil
}

func (d *ebsVolumeDriver) doMount(name string) (string, error) {
	// First, attach the EBS device to the current EC2 instance.
	dev, err := d.attachVolume(name)
	if err != nil {
		return "", err
	}

	// Now auto-generate a random mountpoint.
	mnt := "/mnt/blocker" + uuid.New()

	// Ensure the directory /mnt/blocker/<m> exists.
	if err := os.MkdirAll(mnt, os.ModeDir|0700); err != nil {
		return "", err
	}
	if stat, err := os.Stat(mnt); err != nil || !stat.IsDir() {
		return "", errors.New("Mountpoint creation failed.")
	}

	// Now go ahead and mount the EBS device to the desired mountpoint.
	// TODO: permit the filesystem type in the name.
	// TODO: detect and auto-format unformatted filesystems.
	if err := syscall.Mount(dev, mnt, "ext4", 0, ""); err != nil {
		return "", err
	}

	// And finally set and return it.
	d.volumes[name] = mnt
	return mnt, nil
}

func (d *ebsVolumeDriver) attachVolume(name string) (string, error) {
	dev, err := d.findFreeDeviceForAttach()
	if err != nil {
		return "", err
	}

	if _, err := d.ec2.AttachVolume(&ec2.AttachVolumeInput{
		Device:     aws.String(dev),
		InstanceId: aws.String(d.awsInstanceId),
		VolumeId:   aws.String(name),
	}); err != nil {
		return "", err
	}

	fmt.Printf("Attached EBS volume %v to %v:%v.", name, d.awsInstanceId, dev)
	return dev, nil
}

func (d *ebsVolumeDriver) findFreeDeviceForAttach() (string, error) {
	// Get a list of instance devices.
	volumes, err := d.ec2.DescribeVolumes(&ec2.DescribeVolumesInput{
		Filters: []*ec2.Filter{
			{
				Name: aws.String("attachment.instance-id"),
				Values: []*string{
					aws.String(d.awsInstanceId),
				},
			},
		},
	})
	if err != nil {
		return "", err
	}

	devices := make(map[string]bool)
	for _, volume := range volumes.Volumes {
		if len(volume.Attachments) == 0 {
			continue
		}
		devices[*volume.Attachments[0].Device] = true
	}

	// Now find the first free devices to attach the EBS volume.  See
	// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html
	// for recommended naming scheme (/dev/sd[f-p]).
	for _, c := range "fghijklmnop" {
		dev := "/dev/sd" + string(c)
		if _, exists := devices[dev]; !exists {
			return dev, nil
		}
	}

	return "", errors.New("No devices available for attach: /dev/sd[f-p] taken.")
}

func (d *ebsVolumeDriver) doUnmount(name string) error {
	mnt := d.volumes[name]

	// First unmount the device.
	if err := syscall.Unmount(mnt, 0); err != nil {
		return err
	}

	// Remove the mountpoint from the filesystem.
	if err := os.Remove(mnt); err != nil {
		return err
	}

	// Detach the EBS volume from this AWS instance.
	if err := d.detachVolume(name); err != nil {
		return err
	}

	// Finally clear out the slot and return.
	d.volumes[name] = ""
	return nil
}

func (d *ebsVolumeDriver) detachVolume(name string) error {
	if _, err := d.ec2.DetachVolume(&ec2.DetachVolumeInput{
		InstanceId: aws.String(d.awsInstanceId),
		VolumeId:   aws.String(name),
	}); err != nil {
		return err
	}

	fmt.Printf("Detached EBS volume %v from %v.", name, d.awsInstanceId)
	return nil
}
<commit_msg>Switch UUID packages<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"syscall"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/satori/go.uuid"
)

type ebsVolumeDriver struct {
	ec2                 *ec2.EC2
	ec2meta             *ec2metadata.Client
	awsInstanceId       string
	awsRegion           string
	awsAvailabilityZone string
	volumes             map[string]string
}

func NewEbsVolumeDriver() (VolumeDriver, error) {
	ec2meta := ec2metadata.New(nil)

	if !ec2meta.Available() {
		return nil, errors.New("Not running on an EC2 instance.")
	}

	d := &ebsVolumeDriver{}

	var err error
	d.awsInstanceId, err = ec2meta.GetMetadata("instance-id")
	if err != nil {
		return nil, err
	}

	d.awsRegion, err = ec2meta.Region()
	if err != nil {
		return nil, err
	}

	d.awsAvailabilityZone, err = ec2meta.GetMetadata("placement/availability-zone")
	if err != nil {
		return nil, err
	}

	return &ebsVolumeDriver{
		ec2:     ec2.New(aws.NewConfig().WithRegion(d.awsRegion)),
		ec2meta: ec2meta,
		volumes: make(map[string]string),
	}, nil
}

func (d *ebsVolumeDriver) getEbsInfo(name string) error {
	// Query EC2 to make sure this volume is indeed available.
	volumes, err := d.ec2.DescribeVolumes(&ec2.DescribeVolumesInput{
		VolumeIds: []*string{
			aws.String(name),
		},
	})
	if err != nil {
		return err
	}
	if len(volumes.Volumes) != 1 {
		return errors.New("Cannot find EBS volume.")
	}

	// TODO: check that it's in the right region.

	return nil
}

func (d *ebsVolumeDriver) Create(name string) error {
	m, exists := d.volumes[name]
	if exists {
		// Docker won't always cleanly remove entries.  It's okay so long
		// as the target isn't already mounted by someone else.
		if m != "" {
			return errors.New("Name already in use.")
		}
	}

	d.volumes[name] = ""
	return nil
}

func (d *ebsVolumeDriver) Mount(name string) (string, error) {
	m, exists := d.volumes[name]
	if !exists {
		return "", errors.New("Name not found.")
	}

	if m != "" {
		return "", errors.New("Volume already mounted.")
	}

	return d.doMount(name)
}

func (d *ebsVolumeDriver) Path(name string) (string, error) {
	m, exists := d.volumes[name]
	if !exists {
		return "", errors.New("Name not found.")
	}

	if m == "" {
		return "", errors.New("Volume not mounted.")
	}

	return m, nil
}

func (d *ebsVolumeDriver) Remove(name string) error {
	m, exists := d.volumes[name]
	if !exists {
		return errors.New("Name not found.")
	}

	// If the volume is still mounted, unmount it before removing it.
	if m != "" {
		err := d.doUnmount(name)
		if err != nil {
			return err
		}
	}

	delete(d.volumes, name)
	return nil
}

func (d *ebsVolumeDriver) Unmount(name string) error {
	m, exists := d.volumes[name]
	if !exists {
		return errors.New("Name not found.")
	}

	// If the volume is mounted, go ahead and unmount it.  Ignore requests
	// to unmount volumes that aren't actually mounted.
	if m != "" {
		err := d.doUnmount(name)
		if err != nil {
			return err
		}
	}

	return nil
}

func (d *ebsVolumeDriver) doMount(name string) (string, error) {
	// First, attach the EBS device to the current EC2 instance.
	dev, err := d.attachVolume(name)
	if err != nil {
		return "", err
	}

	// Now auto-generate a random mountpoint.
	mnt := "/mnt/blocker" + uuid.NewV4().String()

	// Ensure the directory /mnt/blocker/<m> exists.
	if err := os.MkdirAll(mnt, os.ModeDir|0700); err != nil {
		return "", err
	}
	if stat, err := os.Stat(mnt); err != nil || !stat.IsDir() {
		return "", errors.New("Mountpoint creation failed.")
	}

	// Now go ahead and mount the EBS device to the desired mountpoint.
	// TODO: permit the filesystem type in the name.
	// TODO: detect and auto-format unformatted filesystems.
	if err := syscall.Mount(dev, mnt, "ext4", 0, ""); err != nil {
		return "", err
	}

	// And finally set and return it.
	d.volumes[name] = mnt
	return mnt, nil
}

func (d *ebsVolumeDriver) attachVolume(name string) (string, error) {
	dev, err := d.findFreeDeviceForAttach()
	if err != nil {
		return "", err
	}

	if _, err := d.ec2.AttachVolume(&ec2.AttachVolumeInput{
		Device:     aws.String(dev),
		InstanceId: aws.String(d.awsInstanceId),
		VolumeId:   aws.String(name),
	}); err != nil {
		return "", err
	}

	fmt.Printf("Attached EBS volume %v to %v:%v.", name, d.awsInstanceId, dev)
	return dev, nil
}

func (d *ebsVolumeDriver) findFreeDeviceForAttach() (string, error) {
	// Get a list of instance devices.
	volumes, err := d.ec2.DescribeVolumes(&ec2.DescribeVolumesInput{
		Filters: []*ec2.Filter{
			{
				Name: aws.String("attachment.instance-id"),
				Values: []*string{
					aws.String(d.awsInstanceId),
				},
			},
		},
	})
	if err != nil {
		return "", err
	}

	devices := make(map[string]bool)
	for _, volume := range volumes.Volumes {
		if len(volume.Attachments) == 0 {
			continue
		}
		devices[*volume.Attachments[0].Device] = true
	}

	// Now find the first free devices to attach the EBS volume.  See
	// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html
	// for recommended naming scheme (/dev/sd[f-p]).
	for _, c := range "fghijklmnop" {
		dev := "/dev/sd" + string(c)
		if _, exists := devices[dev]; !exists {
			return dev, nil
		}
	}

	return "", errors.New("No devices available for attach: /dev/sd[f-p] taken.")
}

func (d *ebsVolumeDriver) doUnmount(name string) error {
	mnt := d.volumes[name]

	// First unmount the device.
	if err := syscall.Unmount(mnt, 0); err != nil {
		return err
	}

	// Remove the mountpoint from the filesystem.
	if err := os.Remove(mnt); err != nil {
		return err
	}

	// Detach the EBS volume from this AWS instance.
	if err := d.detachVolume(name); err != nil {
		return err
	}

	// Finally clear out the slot and return.
	d.volumes[name] = ""
	return nil
}

func (d *ebsVolumeDriver) detachVolume(name string) error {
	if _, err := d.ec2.DetachVolume(&ec2.DetachVolumeInput{
		InstanceId: aws.String(d.awsInstanceId),
		VolumeId:   aws.String(name),
	}); err != nil {
		return err
	}

	fmt.Printf("Detached EBS volume %v from %v.", name, d.awsInstanceId)
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"net"
	"strings"
)

func strcat(s1 string, s2 string) string {
	var buffer bytes.Buffer
	buffer.WriteString(s1)
	buffer.WriteString(s2)
	return buffer.String()
}

func GetUserByNick(nick string) *User {
	nick = strings.ToLower(nick)
	for _, k := range userlist {
		if strings.ToLower(k.nick) == nick {
			return k
		}
	}
	return nil
}

func GetIpFromConn(conn net.Conn) string {
	ip := conn.RemoteAddr().String()
	if !strings.HasPrefix(ip, "[") {
		//IPV4
		return strings.Split(ip, ":")[0]
	} else {
		//IPV6
		ip = strings.Split(ip, "]")[0]
		ip = strings.TrimPrefix(ip, "[")
		return ip
	}
}

func GetChannelByName(name string) *Channel {
	return chanlist[strings.ToLower(name)]
}

func SendToMany(msg string, list []*User) {
	users := make(map[*User]int)
	for j, _ := range list {
		users[j] = 0
	}
	for j, _ := range users {
		j.SendLine(msg)
	}
}

/*
//SendToMany2f sends to all users in list ([]*User), does not check for dupes, accepts in printf format.
func SendToMany2f(list []*User, msg string, args ...interface{}) {
	for _, k := range list {
		k.SendLinef(msg, args...)
	}
}
*/

func ValidChanName(name string) bool {
	if ChanHasBadChars(name) {
		return false
	}
	for _, k := range valid_chan_prefix {
		if strings.HasPrefix(name, k) {
			return true
		}
	}
	return false
}

//IMPORTANT: args must ABSOLUTELY be a valid privmsg command, or this will not work
//validity does not depend on leading ":", I don't care that much
func FormatMessageArgs(args []string) string {
	msg := strings.Join(args[2:], " ")
	msg = strings.TrimPrefix(msg, ":")
	return msg
}

func NickHasBadChars(nick string) bool {
	for _, k := range global_bad_chars {
		if strings.Contains(nick, k) {
			return true
		}
	}
	for _, k := range valid_chan_prefix {
		if strings.Contains(nick, k) {
			return true
		}
	}
	return false
}

func ChanHasBadChars(nick string) bool {
	for _, k := range global_bad_chars {
		if strings.Contains(nick, k) {
			return true
		}
	}
	return false
}

func ChanUserNone(name string) int {
	if GetChannelByName(name) != nil {
		return 1
	} else if GetUserByNick(name) != nil {
		return 2
	} else {
		return 0
	}
}

func WildcardMatch(text string, pattern string) bool {
	cards := strings.Split(pattern, "*")
	for _, card := range cards {
		index := strings.Index(text, card)
		if index == -1 {
			return false
		}
		text = text[index+len(card):]
	}
	return true
}
<commit_msg>remove SendToMany2f, it has no reason to exist with channel.SendLinef()<commit_after>package main

import (
	"bytes"
	"net"
	"strings"
)

func strcat(s1 string, s2 string) string {
	var buffer bytes.Buffer
	buffer.WriteString(s1)
	buffer.WriteString(s2)
	return buffer.String()
}

func GetUserByNick(nick string) *User {
	nick = strings.ToLower(nick)
	for _, k := range userlist {
		if strings.ToLower(k.nick) == nick {
			return k
		}
	}
	return nil
}

func GetIpFromConn(conn net.Conn) string {
	ip := conn.RemoteAddr().String()
	if !strings.HasPrefix(ip, "[") {
		//IPV4
		return strings.Split(ip, ":")[0]
	} else {
		//IPV6
		ip = strings.Split(ip, "]")[0]
		ip = strings.TrimPrefix(ip, "[")
		return ip
	}
}

func GetChannelByName(name string) *Channel {
	return chanlist[strings.ToLower(name)]
}

func SendToMany(msg string, list []*User) {
	users := make(map[*User]int)
	for j, _ := range list {
		users[j] = 0
	}
	for j, _ := range users {
		j.SendLine(msg)
	}
}

func ValidChanName(name string) bool {
	if ChanHasBadChars(name) {
		return false
	}
	for _, k := range valid_chan_prefix {
		if strings.HasPrefix(name, k) {
			return true
		}
	}
	return false
}

//IMPORTANT: args must ABSOLUTELY be a valid privmsg command, or this will not work
//validity does not depend on leading ":", I don't care that much
func FormatMessageArgs(args []string) string {
	msg := strings.Join(args[2:], " ")
	msg = strings.TrimPrefix(msg, ":")
	return msg
}

func NickHasBadChars(nick string) bool {
	for _, k := range global_bad_chars {
		if strings.Contains(nick, k) {
			return true
		}
	}
	for _, k := range valid_chan_prefix {
		if strings.Contains(nick, k) {
			return true
		}
	}
	return false
}

func ChanHasBadChars(nick string) bool {
	for _, k := range global_bad_chars {
		if strings.Contains(nick, k) {
			return true
		}
	}
	return false
}

func ChanUserNone(name string) int {
	if GetChannelByName(name) != nil {
		return 1
	} else if GetUserByNick(name) != nil {
		return 2
	} else {
		return 0
	}
}

func WildcardMatch(text string, pattern string) bool {
	cards := strings.Split(pattern, "*")
	for _, card := range cards {
		index := strings.Index(text, card)
		if index == -1 {
			return false
		}
		text = text[index+len(card):]
	}
	return true
}
<|endoftext|>


<commit_before>package sentry

import (
	"fmt"

	apex "github.com/francoishill/log"
	"github.com/francoishill/log/handlers/level"
	raven "github.com/getsentry/raven-go"
)

//NewLogHandler returns a new logger that logs to sentry
func NewLogHandler(client *raven.Client, logLevel apex.Level) apex.Handler {
	sentryLogger := &logger{client: client}
	return level.New(sentryLogger, logLevel)
}

type logger struct {
	client *raven.Client
}

var severityMapping = [...]raven.Severity{
	apex.DebugLevel:     raven.DEBUG,
	apex.InfoLevel:      raven.INFO,
	apex.NoticeLevel:    raven.WARNING,
	apex.WarnLevel:      raven.WARNING,
	apex.ErrorLevel:     raven.ERROR,
	apex.CriticalLevel:  raven.FATAL,
	apex.AlertLevel:     raven.FATAL,
	apex.EmergencyLevel: raven.FATAL,
}

func (l *logger) HandleLog(e *apex.Entry) error {
	packet := raven.NewPacket(e.Message)
	packet.Init("") //will setup default fields like project, culprit, etc
	packet.Level = severityMapping[e.Level]

	tags := map[string]string{}
	for key, val := range e.Fields {
		tags[key] = fmt.Sprintf("%v", val)
		packet.Extra["full-"+key] = fmt.Sprintf("%+v", val)
	}

	go l.client.Capture(packet, tags)

	return nil
}
<commit_msg>fix: improved sentry logging<commit_after>package sentry

import (
	"fmt"
	"runtime"
	"strings"

	apex "github.com/francoishill/log"
	"github.com/francoishill/log/handlers/level"
	raven "github.com/getsentry/raven-go"
)

//NewLogHandler returns a new logger that logs to sentry
func NewLogHandler(client *raven.Client, logLevel apex.Level) apex.Handler {
	sentryLogger := &logger{client: client}
	return level.New(sentryLogger, logLevel)
}

type logger struct {
	client *raven.Client
}

var severityMapping = [...]raven.Severity{
	apex.DebugLevel:     raven.DEBUG,
	apex.InfoLevel:      raven.INFO,
	apex.NoticeLevel:    raven.WARNING,
	apex.WarnLevel:      raven.WARNING,
	apex.ErrorLevel:     raven.ERROR,
	apex.CriticalLevel:  raven.FATAL,
	apex.AlertLevel:     raven.FATAL,
	apex.EmergencyLevel: raven.FATAL,
}

func (l *logger) HandleLog(e *apex.Entry) error {
	tags := map[string]string{}

	var interfaces []raven.Interface
	if errInterface, hasErr := e.Fields["error"]; hasErr {
		if err, isErrType := errInterface.(error); isErrType {
			// interfaces = append(interfaces, raven.NewException(err, raven.NewStacktrace(0, 3, l.client.IncludePaths())))
			interfaces = append(interfaces, raven.NewException(err, newAutoStacktrace(3, l.client.IncludePaths())))
		}
	}

	for key, val := range e.Fields {
		if key == "error" || key == "stack" {
			//error is added above and stack is not reported to sentry here, the newAutoStacktrace is used above
			continue
		}
		tags[key] = fmt.Sprintf("%v", val)
	}

	packet := raven.NewPacket(e.Message, interfaces...)
	packet.Init("") //will setup default fields like project, culprit, etc
	packet.Level = severityMapping[e.Level]

	go l.client.Capture(packet, tags)

	return nil
}

//newAutoStacktrace is a slight variation of raven.NewStacktrace (skipping some frames based on file path)
func newAutoStacktrace(context int, appPackagePrefixes []string) *raven.Stacktrace {
	var frames []*raven.StacktraceFrame
	for i := 1; ; i++ {
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}

		ignoredPaths := []string{
			"github.com/go-zero-boilerplate/facades/alerting/sentry/logger.go",
			"github.com/francoishill/log/handlers/multi/multi.go",
			"github.com/francoishill/log/handlers/level/level.go",
			"github.com/francoishill/log/logger.go",
			"github.com/francoishill/log/entry.go",
		}

		mustIgnore := false
		for _, i := range ignoredPaths {
			if strings.Contains(strings.ToLower(file), strings.ToLower(i)) {
				mustIgnore = true
				break
			}
		}
		if mustIgnore {
			continue
		}

		frame := raven.NewStacktraceFrame(pc, file, line, context, appPackagePrefixes)
		if frame != nil {
			frames = append(frames, frame)
		}
	}
	// If there are no frames, the entire stacktrace is nil
	if len(frames) == 0 {
		return nil
	}
	// Optimize the path where there's only 1 frame
	if len(frames) == 1 {
		return &raven.Stacktrace{frames}
	}
	// Sentry wants the frames with the oldest first, so reverse them
	for i, j := 0, len(frames)-1; i < j; i, j = i+1, j-1 {
		frames[i], frames[j] = frames[j], frames[i]
	}
	return &raven.Stacktrace{frames}
}
<|endoftext|>


<commit_before>package task

import (
	"MonitorPlat/Framework/log"
	"MonitorPlat/OneDistribute/const"
	"sync"
	"time"
)

const (
	taskLogTarget  = "task"
	taskState_Init = "0"
	taskState_Run  = "1"
	taskState_Stop = "2"
)

//task 容器
type TaskService struct {
	taskMap         map[string]*TaskInfo
	taskMutex       *sync.RWMutex
	logPath         string
	taskHandleMap   map[string]TaskHandle
	taskHandleMutex *sync.RWMutex
}

//Task定义
type TaskInfo struct {
	TimeTicker *time.Ticker
	TaskID     string
	handler    TaskHandle
	Context    *TaskContext
	state      string //匹配 taskState_Init、taskState_Run、taskState_Stop
}

//Task上下文信息
type TaskContext struct {
	TaskID     string
	Interval   int64 //运行间隔时间，单位毫秒
	HandleName string
	TaskData   interface{}
}

func StartNewService() *TaskService {
	service := new(TaskService)
	service.taskMutex = new(sync.RWMutex)
	service.taskHandleMutex = new(sync.RWMutex)
	service.taskHandleMap = make(map[string]TaskHandle)
	service.taskMap = make(map[string]*TaskInfo)

	return service
}

type TaskHandle func(*TaskContext)

//get handler by name
func (service *TaskService) GetHandler(name string) (TaskHandle, bool) {
	service.taskHandleMutex.RLock()
	defer service.taskHandleMutex.RUnlock()
	handler, exists := service.taskHandleMap[name]
	return handler, exists
}

//set task handler with handlername
func (service *TaskService) SetHandler(handlerName string, handler TaskHandle) {
	service.taskHandleMap[handlerName] = handler
}

//创建Task对象
func (service *TaskService) CreateTask(taskID string, interval int64, handlerName string, taskData interface{}) *TaskInfo {
	context := new(TaskContext)
	context.HandleName = handlerName
	context.TaskID = taskID
	context.Interval = interval
	context.TaskData = taskData

	handler, exists := service.GetHandler(handlerName)
	if !exists {
		return nil
	}

	task := new(TaskInfo)
	task.TaskID = context.TaskID
	task.handler = handler
	task.state = taskState_Init
	task.Context = context

	service.taskMutex.Lock()
	service.taskMap[context.TaskID] = task
	service.taskMutex.Unlock()
	return task
}

//start timeticker
func (task *TaskInfo) Start() {
	if task.state == taskState_Init || task.state == taskState_Stop {
		task.state = taskState_Run
		task.TimeTicker = time.NewTicker(time.Duration(task.Context.Interval) * time.Millisecond)
		go func() {
			for {
				select {
				case <-task.TimeTicker.C:
					//TODO:do log
					task.handler(task.Context)
				}
			}
		}()
	}
}

//stop timeticker
func (task *TaskInfo) Stop() {
	if task.state == taskState_Stop {
		task.TimeTicker.Stop()
		task.state = taskState_Stop
	}
}

//remove all task
func (service *TaskService) RemoveAllTask() {
	logger.Info("Task::resetAllTask begin...", constdefine.LogTarget_Task)
	service.StopAllTask()
	service.taskMap = make(map[string]*TaskInfo)
}

//结束所有Task
func (service *TaskService) StopAllTask() {
	logger.Info("Task::StopAllTask begin...", constdefine.LogTarget_Task)
	for k, v := range service.taskMap {
		logger.Info("Task::StopAllTask => "+k, constdefine.LogTarget_Task)
		v.Stop()
	}
	logger.Info("Task::StopAllTask end["+string(len(service.taskMap))+"]", constdefine.LogTarget_Task)
}

//启动所有Task
func (service *TaskService) StartAllTask() {
	logger.Info("Task::StartAllTask begin...", constdefine.LogTarget_Task)
	for _, v := range service.taskMap {
		logger.Info("Task::StartAllTask::StartTask => "+v.TaskID, constdefine.LogTarget_Task)
		v.Start()
	}
	logger.Info("Task::StartAllTask end", constdefine.LogTarget_Task)
}
<commit_msg>fixed import path bug<commit_after>package task

import (
	"github.com/devfeel/tokenserver/framework/log"
	"sync"
	"time"
)

const (
	taskLogTarget  = "task"
	taskState_Init = "0"
	taskState_Run  = "1"
	taskState_Stop = "2"
)

//task 容器
type TaskService struct {
	taskMap         map[string]*TaskInfo
	taskMutex       *sync.RWMutex
	logPath         string
	taskHandleMap   map[string]TaskHandle
	taskHandleMutex *sync.RWMutex
}

//Task定义
type TaskInfo struct {
	TimeTicker *time.Ticker
	TaskID     string
	handler    TaskHandle
	Context    *TaskContext
	state      string //匹配 taskState_Init、taskState_Run、taskState_Stop
}

//Task上下文信息
type TaskContext struct {
	TaskID     string
	Interval   int64 //运行间隔时间，单位毫秒
	HandleName string
	TaskData   interface{}
}

func StartNewService() *TaskService {
	service := new(TaskService)
	service.taskMutex = new(sync.RWMutex)
	service.taskHandleMutex = new(sync.RWMutex)
	service.taskHandleMap = make(map[string]TaskHandle)
	service.taskMap = make(map[string]*TaskInfo)

	return service
}

type TaskHandle func(*TaskContext)

//get handler by name
func (service *TaskService) GetHandler(name string) (TaskHandle, bool) {
	service.taskHandleMutex.RLock()
	defer service.taskHandleMutex.RUnlock()
	handler, exists := service.taskHandleMap[name]
	return handler, exists
}

//set task handler with handlername
func (service *TaskService) SetHandler(handlerName string, handler TaskHandle) {
	service.taskHandleMap[handlerName] = handler
}

//创建Task对象
func (service *TaskService) CreateTask(taskID string, interval int64, handlerName string, taskData interface{}) *TaskInfo {
	context := new(TaskContext)
	context.HandleName = handlerName
	context.TaskID = taskID
	context.Interval = interval
	context.TaskData = taskData

	handler, exists := service.GetHandler(handlerName)
	if !exists {
		return nil
	}

	task := new(TaskInfo)
	task.TaskID = context.TaskID
	task.handler = handler
	task.state = taskState_Init
	task.Context = context

	service.taskMutex.Lock()
	service.taskMap[context.TaskID] = task
	service.taskMutex.Unlock()
	return task
}

//start timeticker
func (task *TaskInfo) Start() {
	if task.state == taskState_Init || task.state == taskState_Stop {
		task.state = taskState_Run
		task.TimeTicker = time.NewTicker(time.Duration(task.Context.Interval) * time.Millisecond)
		go func() {
			for {
				select {
				case <-task.TimeTicker.C:
					//TODO:do log
					task.handler(task.Context)
				}
			}
		}()
	}
}

//stop timeticker
func (task *TaskInfo) Stop() {
	if task.state == taskState_Stop {
		task.TimeTicker.Stop()
		task.state = taskState_Stop
	}
}

//remove all task
func (service *TaskService) RemoveAllTask() {
	logger.Info("Task::resetAllTask begin...", taskLogTarget)
	service.StopAllTask()
	service.taskMap = make(map[string]*TaskInfo)
}

//结束所有Task
func (service *TaskService) StopAllTask() {
	logger.Info("Task::StopAllTask begin...", taskLogTarget)
	for k, v := range service.taskMap {
		logger.Info("Task::StopAllTask => "+k, taskLogTarget)
		v.Stop()
	}
	logger.Info("Task::StopAllTask end["+string(len(service.taskMap))+"]", taskLogTarget)
}

//启动所有Task
func (service *TaskService) StartAllTask() {
	logger.Info("Task::StartAllTask begin...", taskLogTarget)
	for _, v := range service.taskMap {
		logger.Info("Task::StartAllTask::StartTask => "+v.TaskID, taskLogTarget)
		v.Start()
	}
	logger.Info("Task::StartAllTask end", taskLogTarget)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"io"
	"log"
	"os"
	"os/exec"
)

var signal = make(chan int)

func sendSig() {
	signal <- 1
}

func pipeOutput(out io.ReadCloser, dest io.WriteCloser, logBuf *bytes.Buffer) {
	tempBuf := make([]byte, 1024)
	writeErr := error(nil)
	r, readErr := int(0), error(nil)

	defer out.Close()
	defer dest.Close()
	defer sendSig()

	for readErr == nil {
		r, readErr = out.Read(tempBuf)
		logBuf.Write(tempBuf[0:r])

		if r != 0 && writeErr == nil {
			_, writeErr := dest.Write(tempBuf[0:r])
			if writeErr != nil {
				log.Print(writeErr)
			}
		}
	}
}

func main() {
	if len(os.Args) != 3 {
		log.Fatal("Invalid number of arguments. This should never have happend")
	}

	commit := os.Args[1]
	tmpdir := os.Args[2]

	cmd := exec.Command("sudo", "docker", "run", "--rm", "-v", tmpdir+":/app", "coduno/base")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		log.Fatal(err)
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Fatal(err)
	}

	var b1 bytes.Buffer
	var b2 bytes.Buffer
	cmd.Start()
	go pipeOutput(stdout, stdin, &b1)
	go pipeOutput(stderr, stdin, &b2)

	<-signal
	<-signal
	log.Print(b1.String())
	log.Print(b2.String())
	log.Print(commit)
}
<commit_msg>Experimental datastore logging<commit_after>package main

import (
	"bytes"
	"io"
	"log"
	"os"

	"io/ioutil"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	"golang.org/x/oauth2/jwt"
	"google.golang.org/api/datastore/v1beta2"
)

var signal = make(chan int)
var secret []byte
var config *jwt.Config
var service *datastore.Service

func init() {
	err := error(nil)
	secret, err = ioutil.ReadFile("../config/Coduno-6b4d6d5a0f06.json")
	if err != nil {
		panic(err)
	}
	config, err = google.JWTConfigFromJSON(secret, datastore.DatastoreScope)
	if err != nil {
		panic(err)
	}
	client := config.Client(context.Background())
	service, err = datastore.New(client)
	if err != nil {
		panic(err)
	}
}

// LogBuildStart sends info to the datastore, informing that a new build
// started
func LogBuildStart(repo string, commit string, user string) {
	insert := new(datastore.CommitRequest)
	insert.Mode = "NON_TRANSACTIONAL"
	path := make([]*datastore.KeyPathElement, 1)
	path[0] = new(datastore.KeyPathElement)
	path[0].Kind = "testrun"
	mutation := new(datastore.Mutation)
	mutation.InsertAutoId = make([]*datastore.Entity, 1)
	mutation.InsertAutoId[0] = new(datastore.Entity)
	mutation.InsertAutoId[0].Properties = make(map[string]datastore.Property)
	mutation.InsertAutoId[0].Properties["repo"] = datastore.Property{StringValue: repo}
	mutation.InsertAutoId[0].Properties["commit"] = datastore.Property{StringValue: commit}
	mutation.InsertAutoId[0].Properties["user"] = datastore.Property{StringValue: user}
	mutation.InsertAutoId[0].Key = new(datastore.Key)
	mutation.InsertAutoId[0].Key.Path = path

	req := service.Datasets.Commit("coduno", insert)
	ret, err := req.Do()
	if err != nil {
		log.Panic(err)
	}
	log.Print(ret.Header)
}

func sendSig() {
	signal <- 1
}

func pipeOutput(out io.ReadCloser, dest io.WriteCloser, logBuf *bytes.Buffer) {
	tempBuf := make([]byte, 1024)
	writeErr := error(nil)
	r, readErr := int(0), error(nil)

	defer out.Close()
	defer dest.Close()
	defer sendSig()

	for readErr == nil {
		r, readErr = out.Read(tempBuf)
		logBuf.Write(tempBuf[0:r])

		if r != 0 && writeErr == nil {
			_, writeErr := dest.Write(tempBuf[0:r])
			if writeErr != nil {
				log.Print(writeErr)
			}
		}
	}
}

func main() {
	if len(os.Args) != 3 {
		log.Fatal("Invalid number of arguments. This should never have happend")
	}

	commit := os.Args[1]
	tmpdir := os.Args[2]
	LogBuildStart("test", commit, tmpdir)
	/*
		cmd := exec.Command("sudo", "docker", "run", "--rm", "-v", tmpdir+":/app", "coduno/base")
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			log.Fatal(err)
		}
		stderr, err := cmd.StderrPipe()
		if err != nil {
			log.Fatal(err)
		}
		stdin, err := cmd.StdinPipe()
		if err != nil {
			log.Fatal(err)
		}

		var b1 bytes.Buffer
		var b2 bytes.Buffer
		cmd.Start()
		go pipeOutput(stdout, stdin, &b1)
		go pipeOutput(stderr, stdin, &b2)

		<-signal
		<-signal
		log.Print(b1.String())
		log.Print(b2.String())
		log.Print(commit)*/
}
<|endoftext|>


<commit_before>package sched

import (
	"bytes"
	"log"
	"time"

	"github.com/StackExchange/tsaf/conf"
	"github.com/StackExchange/tsaf/expr"
	"github.com/StackExchange/tsaf/third_party/github.com/StackExchange/scollector/opentsdb"
)

func (s *Schedule) Check() {
	s.Lock()
	defer s.Unlock()
	s.runStates = make(map[AlertKey]Status)
	s.cache = opentsdb.NewCache(s.Conf.TsdbHost)
	for _, a := range s.Conf.Alerts {
		s.CheckAlert(a)
	}
	s.CheckUnknown()
	checkNotify := false
	silenced := s.Silenced()
	for ak, status := range s.runStates {
		state := s.Status[ak]
		last := state.Append(status)
		a := s.Conf.Alerts[ak.Name]
		if status > stNormal {
			var subject = new(bytes.Buffer)
			if err := s.ExecuteSubject(subject, a, state); err != nil {
				log.Println(err)
			}
			state.Subject = subject.String()
		}
		// On state increase, clear old notifications and notify current.
		// On state decrease, and if the old alert was already acknowledged, notify current.
		// If the old alert was not acknowledged, do nothing.
		// Do nothing if state did not change.
		notify := func(notifications map[string]*conf.Notification) {
			for _, n := range notifications {
				s.Notify(state, n)
				checkNotify = true
			}
		}
		notifyCurrent := func() {
			state.NeedAck = true
			if _, present := silenced[ak]; present {
				log.Println("SILENCED", ak)
				return
			}
			switch status {
			case stCritical, stUnknown:
				notify(a.CritNotification)
			case stWarning:
				notify(a.WarnNotification)
			}
		}
		clearOld := func() {
			state.NeedAck = false
			delete(s.Notifications, ak)
		}
		if status > last {
			clearOld()
			notifyCurrent()
		} else if status < last {
			if _, hasOld := s.Notifications[ak]; hasOld {
				notifyCurrent()
			}
		}
	}
	if checkNotify {
		s.nc <- true
	}
	s.Save()
}

func (s *Schedule) CheckUnknown() {
	for ak, st := range s.Status {
		t := s.Conf.Alerts[ak.Name].Unknown
		if t == 0 {
			t = s.Conf.Unknown
		}
		if t == 0 {
			continue
		}
		if time.Since(st.Touched) < t {
			continue
		}
		s.runStates[ak] = stUnknown
	}
}

func (s *Schedule) CheckAlert(a *conf.Alert) {
	crits := s.CheckExpr(a, a.Crit, stCritical, nil)
	warns := s.CheckExpr(a, a.Warn, stWarning, crits)
	log.Printf("checking alert %v: %v crits, %v warns", a.Name, len(crits), len(warns))
}

func (s *Schedule) CheckExpr(a *conf.Alert, e *expr.Expr, checkStatus Status, ignore AlertKeys) (alerts AlertKeys) {
	if e == nil {
		return
	}
	results, _, err := e.Execute(s.cache, nil)
	if err != nil {
		// todo: do something here?
		log.Println(err)
		return
	}
Loop:
	for _, r := range results {
		if a.Squelched(r.Group) {
			continue
		}
		ak := NewAlertKey(a.Name, r.Group)
		for _, v := range ignore {
			if ak == v {
				continue Loop
			}
		}
		state := s.Status[ak]
		if state == nil {
			state = &State{
				Alert: ak.Name,
				Tags:  r.Group.Tags(),
				Group: r.Group,
			}
			s.Status[ak] = state
		}
		state.Touch()
		status := checkStatus
		state.Computations = r.Computations
		var n float64
		switch v := r.Value.(type) {
		case expr.Number:
			n = float64(v)
		case expr.Scalar:
			n = float64(v)
		default:
			panic("expected number or scalar")
		}
		if n != 0 {
			state.Expr = e.String()
			alerts = append(alerts, ak)
		} else {
			status = stNormal
		}
		if status > s.runStates[ak] {
			s.runStates[ak] = status
		}
	}
	return
}
<commit_msg>Remove debug print<commit_after>package sched

import (
	"bytes"
	"log"
	"time"

	"github.com/StackExchange/tsaf/conf"
	"github.com/StackExchange/tsaf/expr"
	"github.com/StackExchange/tsaf/third_party/github.com/StackExchange/scollector/opentsdb"
)

func (s *Schedule) Check() {
	s.Lock()
	defer s.Unlock()
	s.runStates = make(map[AlertKey]Status)
	s.cache = opentsdb.NewCache(s.Conf.TsdbHost)
	for _, a := range s.Conf.Alerts {
		s.CheckAlert(a)
	}
	s.CheckUnknown()
	checkNotify := false
	silenced := s.Silenced()
	for ak, status := range s.runStates {
		state := s.Status[ak]
		last := state.Append(status)
		a := s.Conf.Alerts[ak.Name]
		if status > stNormal {
			var subject = new(bytes.Buffer)
			if err := s.ExecuteSubject(subject, a, state); err != nil {
				log.Println(err)
			}
			state.Subject = subject.String()
		}
		// On state increase, clear old notifications and notify current.
		// On state decrease, and if the old alert was already acknowledged, notify current.
		// If the old alert was not acknowledged, do nothing.
		// Do nothing if state did not change.
		notify := func(notifications map[string]*conf.Notification) {
			for _, n := range notifications {
				s.Notify(state, n)
				checkNotify = true
			}
		}
		notifyCurrent := func() {
			state.NeedAck = true
			if _, present := silenced[ak]; present {
				return
			}
			switch status {
			case stCritical, stUnknown:
				notify(a.CritNotification)
			case stWarning:
				notify(a.WarnNotification)
			}
		}
		clearOld := func() {
			state.NeedAck = false
			delete(s.Notifications, ak)
		}
		if status > last {
			clearOld()
			notifyCurrent()
		} else if status < last {
			if _, hasOld := s.Notifications[ak]; hasOld {
				notifyCurrent()
			}
		}
	}
	if checkNotify {
		s.nc <- true
	}
	s.Save()
}

func (s *Schedule) CheckUnknown() {
	for ak, st := range s.Status {
		t := s.Conf.Alerts[ak.Name].Unknown
		if t == 0 {
			t = s.Conf.Unknown
		}
		if t == 0 {
			continue
		}
		if time.Since(st.Touched) < t {
			continue
		}
		s.runStates[ak] = stUnknown
	}
}

func (s *Schedule) CheckAlert(a *conf.Alert) {
	crits := s.CheckExpr(a, a.Crit, stCritical, nil)
	warns := s.CheckExpr(a, a.Warn, stWarning, crits)
	log.Printf("checking alert %v: %v crits, %v warns", a.Name, len(crits), len(warns))
}

func (s *Schedule) CheckExpr(a *conf.Alert, e *expr.Expr, checkStatus Status, ignore AlertKeys) (alerts AlertKeys) {
	if e == nil {
		return
	}
	results, _, err := e.Execute(s.cache, nil)
	if err != nil {
		// todo: do something here?
		log.Println(err)
		return
	}
Loop:
	for _, r := range results {
		if a.Squelched(r.Group) {
			continue
		}
		ak := NewAlertKey(a.Name, r.Group)
		for _, v := range ignore {
			if ak == v {
				continue Loop
			}
		}
		state := s.Status[ak]
		if state == nil {
			state = &State{
				Alert: ak.Name,
				Tags:  r.Group.Tags(),
				Group: r.Group,
			}
			s.Status[ak] = state
		}
		state.Touch()
		status := checkStatus
		state.Computations = r.Computations
		var n float64
		switch v := r.Value.(type) {
		case expr.Number:
			n = float64(v)
		case expr.Scalar:
			n = float64(v)
		default:
			panic("expected number or scalar")
		}
		if n != 0 {
			state.Expr = e.String()
			alerts = append(alerts, ak)
		} else {
			status = stNormal
		}
		if status > s.runStates[ak] {
			s.runStates[ak] = status
		}
	}
	return
}
<|endoftext|>


<commit_before>package sched

import (
	"syscall"
	"unsafe"
)

type Policy uintptr

const (
	Other Policy = 0
	FIFO  Policy = 1
	RR    Policy = 2
	Batch Policy = 3
	Idle  Policy = 5
)

func (p Policy) String() string {
	switch p {
	case Other:
		return "Other"
	case FIFO:
		return "FIFO"
	case RR:
		return "RR"
	case Batch:
		return "Batch"
	case Idle:
		return "Idle"
	}
	return "Unknown"
}

func (p Policy) MinPriority() int {
	r0, _, e := syscall.RawSyscall(
		syscall.SYS_SCHED_GET_PRIORITY_MIN,
		uintptr(p),
		0, 0,
	)
	if e != 0 {
		panic(e)
	}
	return int(r0)
}

func (p Policy) MaxPriority() int {
	r0, _, e := syscall.RawSyscall(
		syscall.SYS_SCHED_GET_PRIORITY_MAX,
		uintptr(p),
		0, 0,
	)
	if e != 0 {
		panic(e)
	}
	return int(r0)
}

type Param struct {
	Priority int
}

func SetPolicy(pid int, policy Policy, param *Param) error {
	_, _, e := syscall.RawSyscall(
		syscall.SYS_SCHED_SETSCHEDULER,
		uintptr(pid),
		uintptr(policy),
		uintptr(unsafe.Pointer(param)),
	)
	if e != 0 {
		return e
	}
	return nil
}

func GetPolicy(pid int) (Policy, error) {
	r0, _, e := syscall.RawSyscall(
		syscall.SYS_SCHED_GETSCHEDULER,
		uintptr(pid),
		0, 0,
	)
	if e != 0 {
		return 0, e
	}
	return Policy(r0), nil
}

func SetParam(pid int, param *Param) error {
	_, _, e := syscall.RawSyscall(
		syscall.SYS_SCHED_SETPARAM,
		uintptr(pid),
		uintptr(unsafe.Pointer(param)),
		0,
	)
	if e != 0 {
		return e
	}
	return nil

}

func GetParam(pid int, param *Param) error {
	_, _, e := syscall.RawSyscall(
		syscall.SYS_SCHED_GETPARAM,
		uintptr(pid),
		uintptr(unsafe.Pointer(param)),
		0,
	)
	if e != 0 {
		return e
	}
	return nil
}
<commit_msg>Doc added<commit_after>// Package sched contains functions for set/get process scheduling parameters
package sched

import (
	"syscall"
	"unsafe"
)

type Policy uintptr

const (
	Other Policy = 0
	FIFO  Policy = 1
	RR    Policy = 2
	Batch Policy = 3
	Idle  Policy = 5
)

func (p Policy) String() string {
	switch p {
	case Other:
		return "Other"
	case FIFO:
		return "FIFO"
	case RR:
		return "RR"
	case Batch:
		return "Batch"
	case Idle:
		return "Idle"
	}
	return "Unknown"
}

func (p Policy) MinPriority() int {
	r0, _, e := syscall.RawSyscall(
		syscall.SYS_SCHED_GET_PRIORITY_MIN,
		uintptr(p),
		0, 0,
	)
	if e != 0 {
		panic(e)
	}
	return int(r0)
}

func (p Policy) MaxPriority() int {
	r0, _, e := syscall.RawSyscall(
		syscall.SYS_SCHED_GET_PRIORITY_MAX,
		uintptr(p),
		0, 0,
	)
	if e != 0 {
		panic(e)
	}
	return int(r0)
}

type Param struct {
	Priority int
}

func SetPolicy(pid int, policy Policy, param *Param) error {
	_, _, e := syscall.RawSyscall(
		syscall.SYS_SCHED_SETSCHEDULER,
		uintptr(pid),
		uintptr(policy),
		uintptr(unsafe.Pointer(param)),
	)
	if e != 0 {
		return e
	}
	return nil
}

func GetPolicy(pid int) (Policy, error) {
	r0, _, e := syscall.RawSyscall(
		syscall.SYS_SCHED_GETSCHEDULER,
		uintptr(pid),
		0, 0,
	)
	if e != 0 {
		return 0, e
	}
	return Policy(r0), nil
}

func SetParam(pid int, param *Param) error {
	_, _, e := syscall.RawSyscall(
		syscall.SYS_SCHED_SETPARAM,
		uintptr(pid),
		uintptr(unsafe.Pointer(param)),
		0,
	)
	if e != 0 {
		return e
	}
	return nil

}

func GetParam(pid int, param *Param) error {
	_, _, e := syscall.RawSyscall(
		syscall.SYS_SCHED_GETPARAM,
		uintptr(pid),
		uintptr(unsafe.Pointer(param)),
		0,
	)
	if e != 0 {
		return e
	}
	return nil
}
<|endoftext|>


<commit_before>package transport

import (
	"bufio"
	"context"
	pb "github.com/Randomsock5/tcptunnel/proto"
	"io"
	"log"
	"net"
	"time"
)

func handleErr(err error) {
	if err != nil {
		panic(err)
	}
}

func handleEOF(err error, ch chan error) {
	if err == io.EOF {
		close(ch)
		panic(nil)
	}
}

func recoverHandle(ch chan error) {
	if rec := recover(); rec != nil {
		err := rec.(error)
		ch <- err
	}
}

type proxyService struct {
	forward string
}

func (s *proxyService) Stream(stream pb.ProxyService_StreamServer) error {
	forwardConn, err := net.Dial("tcp", s.forward)
	if err != nil {
		log.Println(err)
		return err
	}
	defer forwardConn.Close()
	wait1 := make(chan error)
	wait2 := make(chan error)

	//
	go func() {
		defer recoverHandle(wait1)

		for {
			buf := make([]byte, 768)
			i, err := forwardConn.Read(buf)
			handleEOF(err, wait1)
			handleErr(err)

			var payload pb.Payload
			payload.Data = buf[:i]

			err = stream.Send(&payload)
			handleEOF(err, wait1)
			handleErr(err)
		}
	}()

	go func() {
		defer recoverHandle(wait2)
		writeBuff := bufio.NewWriter(forwardConn)

		for {
			payload, err := stream.Recv()
			handleEOF(err, wait2)
			handleErr(err)

			data := payload.GetData()
			_, err = writeBuff.Write(data)
			handleEOF(err, wait2)
			handleErr(err)

			err = writeBuff.Flush()
			handleEOF(err, wait2)
			handleErr(err)
		}
	}()

	select {
	case err = <-wait1:
		if err != nil {
			log.Println(err)
			return err
		}

	case err = <-wait2:
		if err != nil {
			log.Println(err)
			return err
		}
	}

	return nil
}

func NewServer(forward string) pb.ProxyServiceServer {
	s := &proxyService{forward: forward}
	return s
}

func ClientProxyService(conn net.Conn, client pb.ProxyServiceClient) {
	defer conn.Close()
	wait1 := make(chan error)
	wait2 := make(chan error)

	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()
	stream, err := client.Stream(ctx)
	if err != nil {
		log.Println(err)
		return
	}
	defer stream.CloseSend()

	//
	go func() {
		defer recoverHandle(wait1)

		for {
			buf := make([]byte, 768)
			i, err := conn.Read(buf)
			handleEOF(err, wait1)
			handleErr(err)

			var payload pb.Payload
			payload.Data = buf[:i]

			err = stream.Send(&payload)
			handleEOF(err, wait1)
			handleErr(err)
		}
	}()

	go func() {
		defer recoverHandle(wait2)
		writeBuff := bufio.NewWriter(conn)

		for {
			payload, err := stream.Recv()
			handleEOF(err, wait2)
			handleErr(err)

			data := payload.GetData()
			_, err = writeBuff.Write(data)
			handleEOF(err, wait2)
			handleErr(err)

			err = writeBuff.Flush()
			handleEOF(err, wait2)
			handleErr(err)
		}
	}()

	select {
	case err = <-wait1:
		if err != nil {
			log.Println(err)
			return
		}

	case err = <-wait2:
		if err != nil {
			log.Println(err)
			return
		}
	}

	return
}
<commit_msg>fix client bug<commit_after>package transport

import (
	"bufio"
	"context"
	pb "github.com/Randomsock5/tcptunnel/proto"
	"log"
	"net"
	"time"
)

func handleErr(err error) {
	if err != nil {
		panic(err)
	}
}

func recoverHandle(ch chan error) {
	if rec := recover(); rec != nil {
		err := rec.(error)
		ch <- err
	}
	close(ch)
}

type proxyService struct {
	forward string
}

func (s *proxyService) Stream(stream pb.ProxyService_StreamServer) error {
	forwardConn, err := net.Dial("tcp", s.forward)
	if err != nil {
		log.Println(err)
		return err
	}
	defer forwardConn.Close()
	wait1 := make(chan error)
	wait2 := make(chan error)
	defer close(wait1)
	defer close(wait2)

	//
	go func() {
		defer recoverHandle(wait1)

		for {
			buf := make([]byte, 768)
			i, err := forwardConn.Read(buf)
			handleErr(err)

			var payload pb.Payload
			payload.Data = buf[:i]

			err = stream.Send(&payload)
			handleErr(err)
		}
	}()

	go func() {
		defer recoverHandle(wait2)
		writeBuff := bufio.NewWriter(forwardConn)

		for {
			payload, err := stream.Recv()
			handleErr(err)

			data := payload.GetData()
			_, err = writeBuff.Write(data)
			handleErr(err)

			err = writeBuff.Flush()
			handleErr(err)
		}
	}()

	select {
	case err = <-wait1:
		if err != nil {
			log.Println(err)
			return err
		}

	case err = <-wait2:
		if err != nil {
			log.Println(err)
			return err
		}
	}

	return nil
}

func NewServer(forward string) pb.ProxyServiceServer {
	s := &proxyService{forward: forward}
	return s
}

func ClientProxyService(conn net.Conn, client pb.ProxyServiceClient) {
	defer conn.Close()
	wait1 := make(chan error)
	wait2 := make(chan error)
	defer close(wait1)
	defer close(wait2)

	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()
	stream, err := client.Stream(ctx)
	if err != nil {
		log.Println(err)
		return
	}
	defer stream.CloseSend()

	go func() {
		defer recoverHandle(wait1)

		for {
			buf := make([]byte, 768)
			i, err := conn.Read(buf)
			handleErr(err)

			var payload pb.Payload
			payload.Data = buf[:i]

			err = stream.Send(&payload)
			handleErr(err)
		}
	}()

	go func() {
		defer recoverHandle(wait2)
		writeBuff := bufio.NewWriter(conn)

		for {
			payload, err := stream.Recv()
			handleErr(err)

			data := payload.GetData()
			_, err = writeBuff.Write(data)
			handleErr(err)

			err = writeBuff.Flush()
			handleErr(err)
		}
	}()

	select {
	case err = <-wait1:
		if err != nil {
			log.Println(err)
			return
		}

	case err = <-wait2:
		if err != nil {
			log.Println(err)
			return
		}
	}

	return
}
<|endoftext|>


<commit_before>// Package websocketproxy is a reverse proxy for WebSocket connections.
package websocketproxy

import (
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"strings"

	"github.com/gorilla/websocket"
)

var (
	// DefaultUpgrader specifies the parameters for upgrading an HTTP
	// connection to a WebSocket connection.
	DefaultUpgrader = &websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
	}

	// DefaultDialer is a dialer with all fields set to the default zero values.
	DefaultDialer = websocket.DefaultDialer
)

// WebsocketProxy is an HTTP Handler that takes an incoming WebSocket
// connection and proxies it to another server.
type WebsocketProxy struct {
	// Backend returns the backend URL which the proxy uses to reverse proxy
	// the incoming WebSocket connection. Request is the initial incoming and
	// unmodified request.
	Backend func(*http.Request) *url.URL

	// Upgrader specifies the parameters for upgrading a incoming HTTP
	// connection to a WebSocket connection. If nil, DefaultUpgrader is used.
	Upgrader *websocket.Upgrader

	//  Dialer contains options for connecting to the backend WebSocket server.
	//  If nil, DefaultDialer is used.
	Dialer *websocket.Dialer
}

// ProxyHandler returns a new http.Handler interface that reverse proxies the
// request to the given target.
func ProxyHandler(target *url.URL) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		NewProxy(target).ServeHTTP(rw, req)
	})
}

// NewProxy returns a new Websocket reverse proxy that rewrites the
// URL's to the scheme, host and base path provider in target.
func NewProxy(target *url.URL) *WebsocketProxy {
	backend := func(r *http.Request) *url.URL { return target }
	return &WebsocketProxy{Backend: backend}
}

func (w *WebsocketProxy) CloseNotify() {
}

// ServeHTTP implements the http.Handler that proxies WebSocket connections.
func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	backendURL := w.Backend(req)
	if backendURL == nil {
		log.Println("websocketproxy: backend URL is nil")
		http.Error(rw, "internal server error", http.StatusInternalServerError)
		return
	}

	dialer := w.Dialer
	if w.Dialer == nil {
		dialer = DefaultDialer
	}

	// Pass headers from the incoming request to the dialer to forward them to
	// the final destinations.
	h := http.Header{}
	h.Add("Origin", req.Header.Get("Origin"))
	protocols := req.Header["Sec-WebSocket-Protocol"]
	for _, prot := range protocols {
		h.Add("Sec-WebSocket-Protocol", prot)
	}
	cookies := req.Header["Cookie"]
	for _, cookie := range cookies {
		h.Add("Cookie", cookie)
	}

	// Pass X-Forwarded-For headers too, code below is a part of
	// httputil.ReverseProxy. See http://en.wikipedia.org/wiki/X-Forwarded-For
	// for more information
	if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
		// If we aren't the first proxy retain prior
		// X-Forwarded-For information as a comma+space
		// separated list and fold multiple headers into one.
		if prior, ok := req.Header["X-Forwarded-For"]; ok {
			clientIP = strings.Join(prior, ", ") + ", " + clientIP
		}
		h.Set("X-Forwarded-For", clientIP)
	}

	// Set the originating protocol of the incoming HTTP request. The SSL might
	// be terminated on our site and because we doing proxy adding this would
	// be helpful for applications on the backend.
	h.Set("X-Forwarded-Proto", "http")
	if req.TLS != nil {
		h.Set("X-Forwarded-Proto", "https")
	}

	// Connect to the backend URL, also pass the headers we prepared above.
	// TODO: support multiplexing on the same backend connection instead of
	// opening a new TCP connection time for each request.
	connBackend, resp, err := dialer.Dial(backendURL.String(), h)
	if err != nil {
		log.Printf("websocketproxy: couldn't dial to remote backend url %s\n", err)
		return
	}
	defer connBackend.Close()

	upgrader := w.Upgrader
	if w.Upgrader == nil {
		upgrader = DefaultUpgrader
	}

	// Only pass those headers to the upgrader.
	upgradeHeader := http.Header{}
	upgradeHeader.Add("Sec-WebSocket-Protocol", resp.Header.Get("Sec-WebSocket-Protocol"))
	upgradeHeader.Add("Set-Cookie", resp.Header.Get("Set-Cookie"))

	// Now upgrade the existing incoming request to a WebSocket connection.
	// Also pass the header that we gathered from the Dial handshake.
	connPub, err := upgrader.Upgrade(rw, req, upgradeHeader)
	if err != nil {
		log.Printf("websocketproxy: couldn't upgrade %s\n", err)
		return
	}
	defer connPub.Close()

	errc := make(chan error, 2)
	cp := func(dst io.Writer, src io.Reader) {
		_, err := io.Copy(dst, src)
		errc <- err
	}

	// Start our proxy now, everything is ready...
	go cp(connBackend.UnderlyingConn(), connPub.UnderlyingConn())
	go cp(connPub.UnderlyingConn(), connBackend.UnderlyingConn())
	<-errc
}
<commit_msg>Add more docs<commit_after>// Package websocketproxy is a reverse proxy for WebSocket connections.
package websocketproxy

import (
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"strings"

	"github.com/gorilla/websocket"
)

var (
	// DefaultUpgrader specifies the parameters for upgrading an HTTP
	// connection to a WebSocket connection.
	DefaultUpgrader = &websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
	}

	// DefaultDialer is a dialer with all fields set to the default zero values.
	DefaultDialer = websocket.DefaultDialer
)

// WebsocketProxy is an HTTP Handler that takes an incoming WebSocket
// connection and proxies it to another server.
type WebsocketProxy struct {
	// Backend returns the backend URL which the proxy uses to reverse proxy
	// the incoming WebSocket connection. Request is the initial incoming and
	// unmodified request.
	Backend func(*http.Request) *url.URL

	// Upgrader specifies the parameters for upgrading a incoming HTTP
	// connection to a WebSocket connection. If nil, DefaultUpgrader is used.
	Upgrader *websocket.Upgrader

	//  Dialer contains options for connecting to the backend WebSocket server.
	//  If nil, DefaultDialer is used.
	Dialer *websocket.Dialer
}

// ProxyHandler returns a new http.Handler interface that reverse proxies the
// request to the given target.
func ProxyHandler(target *url.URL) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		NewProxy(target).ServeHTTP(rw, req)
	})
}

// NewProxy returns a new Websocket reverse proxy that rewrites the
// URL's to the scheme, host and base path provider in target.
func NewProxy(target *url.URL) *WebsocketProxy {
	backend := func(r *http.Request) *url.URL { return target }
	return &WebsocketProxy{Backend: backend}
}

func (w *WebsocketProxy) CloseNotify() {
}

// ServeHTTP implements the http.Handler that proxies WebSocket connections.
func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	backendURL := w.Backend(req)
	if backendURL == nil {
		log.Println("websocketproxy: backend URL is nil")
		http.Error(rw, "internal server error", http.StatusInternalServerError)
		return
	}

	dialer := w.Dialer
	if w.Dialer == nil {
		dialer = DefaultDialer
	}

	// Pass headers from the incoming request to the dialer to forward them to
	// the final destinations.
	h := http.Header{}
	h.Add("Origin", req.Header.Get("Origin"))
	protocols := req.Header["Sec-WebSocket-Protocol"]
	for _, prot := range protocols {
		h.Add("Sec-WebSocket-Protocol", prot)
	}
	cookies := req.Header["Cookie"]
	for _, cookie := range cookies {
		h.Add("Cookie", cookie)
	}

	// Pass X-Forwarded-For headers too, code below is a part of
	// httputil.ReverseProxy. See http://en.wikipedia.org/wiki/X-Forwarded-For
	// for more information
	// TODO: use RFC7239 http://tools.ietf.org/html/rfc7239
	if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
		// If we aren't the first proxy retain prior
		// X-Forwarded-For information as a comma+space
		// separated list and fold multiple headers into one.
		if prior, ok := req.Header["X-Forwarded-For"]; ok {
			clientIP = strings.Join(prior, ", ") + ", " + clientIP
		}
		h.Set("X-Forwarded-For", clientIP)
	}

	// Set the originating protocol of the incoming HTTP request. The SSL might
	// be terminated on our site and because we doing proxy adding this would
	// be helpful for applications on the backend.
	h.Set("X-Forwarded-Proto", "http")
	if req.TLS != nil {
		h.Set("X-Forwarded-Proto", "https")
	}

	// Connect to the backend URL, also pass the headers we prepared above.
	// TODO: support multiplexing on the same backend connection instead of
	// opening a new TCP connection time for each request. This should be
	// optional.
	connBackend, resp, err := dialer.Dial(backendURL.String(), h)
	if err != nil {
		log.Printf("websocketproxy: couldn't dial to remote backend url %s\n", err)
		return
	}
	defer connBackend.Close()

	upgrader := w.Upgrader
	if w.Upgrader == nil {
		upgrader = DefaultUpgrader
	}

	// Only pass those headers to the upgrader.
	upgradeHeader := http.Header{}
	upgradeHeader.Add("Sec-WebSocket-Protocol", resp.Header.Get("Sec-WebSocket-Protocol"))
	upgradeHeader.Add("Set-Cookie", resp.Header.Get("Set-Cookie"))

	// Now upgrade the existing incoming request to a WebSocket connection.
	// Also pass the header that we gathered from the Dial handshake.
	connPub, err := upgrader.Upgrade(rw, req, upgradeHeader)
	if err != nil {
		log.Printf("websocketproxy: couldn't upgrade %s\n", err)
		return
	}
	defer connPub.Close()

	errc := make(chan error, 2)
	cp := func(dst io.Writer, src io.Reader) {
		_, err := io.Copy(dst, src)
		errc <- err
	}

	// Start our proxy now, everything is ready...
	go cp(connBackend.UnderlyingConn(), connPub.UnderlyingConn())
	go cp(connPub.UnderlyingConn(), connBackend.UnderlyingConn())
	<-errc
}
<|endoftext|>


<commit_before>package websockets

import (
	"net/http"

	"github.com/gorilla/websocket"
	"github.com/ian-kent/go-log/log"
)

type Hub struct {
	upgrader       websocket.Upgrader
	connections    map[*connection]bool
	messages       chan interface{}
	registerChan   chan *connection
	unregisterChan chan *connection
}

func NewHub() *Hub {
	hub := &Hub{
		upgrader: websocket.Upgrader{
			ReadBufferSize:  256,
			WriteBufferSize: 4096,
		},
		connections:    make(map[*connection]bool),
		messages:       make(chan interface{}),
		registerChan:   make(chan *connection),
		unregisterChan: make(chan *connection),
	}
	go hub.run()
	return hub
}

func (h *Hub) run() {
	for {
		select {
		case c := <-h.registerChan:
			h.connections[c] = true
		case c := <-h.unregisterChan:
			h.unregister(c)
		case m := <-h.messages:
			for c := range h.connections {
				select {
				case c.send <- m:
				default:
					h.unregister(c)
				}
			}
		}
	}
}

func (h *Hub) unregister(c *connection) {
	if _, ok := h.connections[c]; ok {
		close(c.send)
		delete(h.connections, c)
	}
}

func (h *Hub) Serve(w http.ResponseWriter, r *http.Request) {
	ws, err := h.upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}
	c := &connection{hub: h, ws: ws, send: make(chan interface{}, 256)}
	h.registerChan <- c
	go c.writeLoop()
	go c.readLoop()
}

func (h *Hub) Broadcast(data interface{}) {
	h.messages <- data
}
<commit_msg>Allow cross-origin websocket requests<commit_after>package websockets

import (
	"net/http"

	"github.com/gorilla/websocket"
	"github.com/ian-kent/go-log/log"
)

type Hub struct {
	upgrader       websocket.Upgrader
	connections    map[*connection]bool
	messages       chan interface{}
	registerChan   chan *connection
	unregisterChan chan *connection
}

func NewHub() *Hub {
	hub := &Hub{
		upgrader: websocket.Upgrader{
			ReadBufferSize:  256,
			WriteBufferSize: 4096,
			CheckOrigin: func(r *http.Request) bool {
				return true
			},
		},
		connections:    make(map[*connection]bool),
		messages:       make(chan interface{}),
		registerChan:   make(chan *connection),
		unregisterChan: make(chan *connection),
	}
	go hub.run()
	return hub
}

func (h *Hub) run() {
	for {
		select {
		case c := <-h.registerChan:
			h.connections[c] = true
		case c := <-h.unregisterChan:
			h.unregister(c)
		case m := <-h.messages:
			for c := range h.connections {
				select {
				case c.send <- m:
				default:
					h.unregister(c)
				}
			}
		}
	}
}

func (h *Hub) unregister(c *connection) {
	if _, ok := h.connections[c]; ok {
		close(c.send)
		delete(h.connections, c)
	}
}

func (h *Hub) Serve(w http.ResponseWriter, r *http.Request) {
	ws, err := h.upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}
	c := &connection{hub: h, ws: ws, send: make(chan interface{}, 256)}
	h.registerChan <- c
	go c.writeLoop()
	go c.readLoop()
}

func (h *Hub) Broadcast(data interface{}) {
	h.messages <- data
}
<|endoftext|>