<commit_before>package session

import (
	"crypto/rand"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/13pinj/todoapp/Godeps/_workspace/src/github.com/gin-gonic/gin"
	"github.com/13pinj/todoapp/models"
)

type dbSession struct {
	SID        string `gorm:"primary_key"`
	CreatedAt  time.Time
	IntMap     map[string]int `sql:"-"`
	EncodedMap string
}

func (s *dbSession) ID() string {
	return s.SID
}

func (s *dbSession) GetInt(key string) int {
	return s.IntMap[key]
}

func (s *dbSession) SetInt(key string, val int) {
	if key == "" {
		return
	}
	s.IntMap[key] = val
	s.encode()
	models.DB.Save(s)
}

func (s *dbSession) encode() {
	val := url.Values{}
	for k, v := range s.IntMap {
		vs := strconv.Itoa(v)
		val.Set(k, vs)
	}
	s.EncodedMap = val.Encode()
}

func (s *dbSession) decode() {
	s.IntMap = make(map[string]int)
	val, err := url.ParseQuery(s.EncodedMap)
	if err != nil {
		return
	}
	for k := range val {
		v, err := strconv.Atoi(val.Get(k))
		if err != nil {
			continue
		}
		s.IntMap[k] = v
	}
}

func dbHasSession(c *gin.Context) (string, bool) {
	st, err := c.Request.Cookie("SessID")
	if err != nil {
		return "", false
	}
	return st.Value, true
}

func dbSessionInit(c *gin.Context) *dbSession {
	session := dbSession{}

	hash := make([]byte, 6)
	rand.Read(hash)

	session.SID = fmt.Sprintf("%x", hash)
	session.IntMap = make(map[string]int)

	models.DB.Create(&session)

	http.SetCookie(c.Writer, &http.Cookie{
		Name:  "SessID",
		Value: session.SID,
		Path:  "/",
	})
	return &session
}

func dbFromContext(c *gin.Context) *dbSession {
	iid, exists := c.Get("session_id")
	if exists {
		id := iid.(string)
		s := &dbSession{}
		err := models.DB.Where("s_id = ?", id).Find(s).Error
		if err == nil {
			s.decode()
			return s
		}
	}
	key, ok := dbHasSession(c)
	if ok {
		s := &dbSession{}
		err := models.DB.Where("s_id = ?", key).Find(s).Error
		if err == nil {
			s.decode()
			c.Set("session_id", key)
			return s
		}
	}
	ses := dbSessionInit(c)
	c.Set("session_id", ses.ID())
	return ses
}

func init() {
	models.DB.AutoMigrate(&dbSession{})
}
<commit_msg>Make sessions live longer<commit_after>package session

import (
	"crypto/rand"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/13pinj/todoapp/Godeps/_workspace/src/github.com/gin-gonic/gin"
	"github.com/13pinj/todoapp/models"
)

type dbSession struct {
	SID        string `gorm:"primary_key"`
	CreatedAt  time.Time
	IntMap     map[string]int `sql:"-"`
	EncodedMap string
}

func (s *dbSession) ID() string {
	return s.SID
}

func (s *dbSession) GetInt(key string) int {
	return s.IntMap[key]
}

func (s *dbSession) SetInt(key string, val int) {
	if key == "" {
		return
	}
	s.IntMap[key] = val
	s.encode()
	models.DB.Save(s)
}

func (s *dbSession) encode() {
	val := url.Values{}
	for k, v := range s.IntMap {
		vs := strconv.Itoa(v)
		val.Set(k, vs)
	}
	s.EncodedMap = val.Encode()
}

func (s *dbSession) decode() {
	s.IntMap = make(map[string]int)
	val, err := url.ParseQuery(s.EncodedMap)
	if err != nil {
		return
	}
	for k := range val {
		v, err := strconv.Atoi(val.Get(k))
		if err != nil {
			continue
		}
		s.IntMap[k] = v
	}
}

func dbHasSession(c *gin.Context) (string, bool) {
	st, err := c.Request.Cookie("SessID")
	if err != nil {
		return "", false
	}
	return st.Value, true
}

func dbSessionInit(c *gin.Context) *dbSession {
	session := dbSession{}

	hash := make([]byte, 6)
	rand.Read(hash)

	session.SID = fmt.Sprintf("%x", hash)
	session.IntMap = make(map[string]int)

	models.DB.Create(&session)

	http.SetCookie(c.Writer, &http.Cookie{
		Name:    "SessID",
		Value:   session.SID,
		Path:    "/",
		Expires: time.Now().AddDate(10, 0, 0),
	})
	return &session
}

func dbFromContext(c *gin.Context) *dbSession {
	iid, exists := c.Get("session_id")
	if exists {
		id := iid.(string)
		s := &dbSession{}
		err := models.DB.Where("s_id = ?", id).Find(s).Error
		if err == nil {
			s.decode()
			return s
		}
	}
	key, ok := dbHasSession(c)
	if ok {
		s := &dbSession{}
		err := models.DB.Where("s_id = ?", key).Find(s).Error
		if err == nil {
			s.decode()
			c.Set("session_id", key)
			return s
		}
	}
	ses := dbSessionInit(c)
	c.Set("session_id", ses.ID())
	return ses
}

func init() {
	models.DB.AutoMigrate(&dbSession{})
}
<|endoftext|>


<commit_before>package filesys

import (
	"bytes"
	"context"
	"fmt"
	"time"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/operation"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"sync"
)

type ContinuousDirtyPages struct {
	hasData bool
	Offset  int64
	Size    int64
	Data    []byte
	f       *File
	lock    sync.Mutex
}

func newDirtyPages(file *File) *ContinuousDirtyPages {
	return &ContinuousDirtyPages{
		Data: make([]byte, file.wfs.option.ChunkSizeLimit),
		f:    file,
	}
}

func (pages *ContinuousDirtyPages) AddPage(ctx context.Context, offset int64, data []byte) (chunks []*filer_pb.FileChunk, err error) {

	pages.lock.Lock()
	defer pages.lock.Unlock()

	var chunk *filer_pb.FileChunk

	if len(data) > len(pages.Data) {
		// this is more than what buffer can hold.

		// flush existing
		if chunk, err = pages.saveExistingPagesToStorage(ctx); err == nil {
			if chunk != nil {
				glog.V(4).Infof("%s/%s flush existing [%d,%d)", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size))
				chunks = append(chunks, chunk)
			}
		} else {
			glog.V(0).Infof("%s/%s failed to flush1 [%d,%d): %v", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size), err)
			return
		}
		pages.Size = 0
		pages.Offset = 0

		// flush the big page
		if chunk, err = pages.saveToStorage(ctx, data, offset); err == nil {
			if chunk != nil {
				glog.V(4).Infof("%s/%s flush big request [%d,%d)", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size))
				chunks = append(chunks, chunk)
			}
		} else {
			glog.V(0).Infof("%s/%s failed to flush2 [%d,%d): %v", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size), err)
			return
		}

		return
	}

	if offset < pages.Offset || offset >= pages.Offset+int64(len(pages.Data)) ||
		pages.Offset+int64(len(pages.Data)) < offset+int64(len(data)) {
		// if the data is out of range,
		// or buffer is full if adding new data,
		// flush current buffer and add new data

		// println("offset", offset, "size", len(data), "existing offset", pages.Offset, "size", pages.Size)

		if chunk, err = pages.saveExistingPagesToStorage(ctx); err == nil {
			if chunk != nil {
				glog.V(4).Infof("%s/%s add save [%d,%d)", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size))
				chunks = append(chunks, chunk)
			}
		} else {
			glog.V(0).Infof("%s/%s add save [%d,%d): %v", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size), err)
			return
		}
		pages.Offset = offset
		copy(pages.Data, data)
		pages.Size = int64(len(data))
		return
	}

	if offset != pages.Offset+pages.Size {
		// when this happens, debug shows the data overlapping with existing data is empty
		// the data is not just append
		copy(pages.Data[pages.Size:], data[pages.Offset+pages.Size-offset:])
	} else {
		copy(pages.Data[offset-pages.Offset:], data)
	}
	pages.Size = max(pages.Size, offset+int64(len(data))-pages.Offset)

	return
}

func (pages *ContinuousDirtyPages) FlushToStorage(ctx context.Context) (chunk *filer_pb.FileChunk, err error) {

	pages.lock.Lock()
	defer pages.lock.Unlock()

	if pages.Size == 0 {
		return nil, nil
	}

	if chunk, err = pages.saveExistingPagesToStorage(ctx); err == nil {
		pages.Size = 0
		pages.Offset = 0
		if chunk != nil {
			glog.V(4).Infof("%s/%s flush [%d,%d)", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size))
		}
	}
	return
}

func (pages *ContinuousDirtyPages) saveExistingPagesToStorage(ctx context.Context) (*filer_pb.FileChunk, error) {

	if pages.Size == 0 {
		return nil, nil
	}

	return pages.saveToStorage(ctx, pages.Data[:pages.Size], pages.Offset)
}

func (pages *ContinuousDirtyPages) saveToStorage(ctx context.Context, buf []byte, offset int64) (*filer_pb.FileChunk, error) {

	var fileId, host string

	if err := pages.f.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.AssignVolumeRequest{
			Count:       1,
			Replication: pages.f.wfs.option.Replication,
			Collection:  pages.f.wfs.option.Collection,
			TtlSec:      pages.f.wfs.option.TtlSec,
			DataCenter:  pages.f.wfs.option.DataCenter,
		}

		resp, err := client.AssignVolume(ctx, request)
		if err != nil {
			glog.V(0).Infof("assign volume failure %v: %v", request, err)
			return err
		}

		fileId, host = resp.FileId, resp.Url

		return nil
	}); err != nil {
		return nil, fmt.Errorf("filerGrpcAddress assign volume: %v", err)
	}

	fileUrl := fmt.Sprintf("http://%s/%s", host, fileId)
	bufReader := bytes.NewReader(buf)
	uploadResult, err := operation.Upload(fileUrl, pages.f.Name, bufReader, false, "application/octet-stream", nil, "")
	if err != nil {
		glog.V(0).Infof("upload data %v to %s: %v", pages.f.Name, fileUrl, err)
		return nil, fmt.Errorf("upload data: %v", err)
	}
	if uploadResult.Error != "" {
		glog.V(0).Infof("upload failure %v to %s: %v", pages.f.Name, fileUrl, err)
		return nil, fmt.Errorf("upload result: %v", uploadResult.Error)
	}

	return &filer_pb.FileChunk{
		FileId: fileId,
		Offset: offset,
		Size:   uint64(len(buf)),
		Mtime:  time.Now().UnixNano(),
	}, nil

}

func max(x, y int64) int64 {
	if x > y {
		return x
	}
	return y
}
<commit_msg>simplifying logic to avoid handling non continuous writes<commit_after>package filesys

import (
	"bytes"
	"context"
	"fmt"
	"time"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/operation"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"sync"
)

type ContinuousDirtyPages struct {
	hasData bool
	Offset  int64
	Size    int64
	Data    []byte
	f       *File
	lock    sync.Mutex
}

func newDirtyPages(file *File) *ContinuousDirtyPages {
	return &ContinuousDirtyPages{
		Data: make([]byte, file.wfs.option.ChunkSizeLimit),
		f:    file,
	}
}

func (pages *ContinuousDirtyPages) AddPage(ctx context.Context, offset int64, data []byte) (chunks []*filer_pb.FileChunk, err error) {

	pages.lock.Lock()
	defer pages.lock.Unlock()

	var chunk *filer_pb.FileChunk

	if len(data) > len(pages.Data) {
		// this is more than what buffer can hold.
		return pages.flushAndSave(ctx, offset, data)
	}

	if offset < pages.Offset || offset >= pages.Offset+int64(len(pages.Data)) ||
		pages.Offset+int64(len(pages.Data)) < offset+int64(len(data)) {
		// if the data is out of range,
		// or buffer is full if adding new data,
		// flush current buffer and add new data

		// println("offset", offset, "size", len(data), "existing offset", pages.Offset, "size", pages.Size)

		if chunk, err = pages.saveExistingPagesToStorage(ctx); err == nil {
			if chunk != nil {
				glog.V(4).Infof("%s/%s add save [%d,%d)", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size))
				chunks = append(chunks, chunk)
			}
		} else {
			glog.V(0).Infof("%s/%s add save [%d,%d): %v", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size), err)
			return
		}
		pages.Offset = offset
		copy(pages.Data, data)
		pages.Size = int64(len(data))
		return
	}

	if offset != pages.Offset+pages.Size {
		// when this happens, debug shows the data overlapping with existing data is empty
		// the data is not just append
		return pages.flushAndSave(ctx, offset, data)
	}

	copy(pages.Data[offset-pages.Offset:], data)
	pages.Size = max(pages.Size, offset+int64(len(data))-pages.Offset)

	return
}

func (pages *ContinuousDirtyPages) flushAndSave(ctx context.Context, offset int64, data []byte) (chunks []*filer_pb.FileChunk, err error) {

	var chunk *filer_pb.FileChunk

	// flush existing
	if chunk, err = pages.saveExistingPagesToStorage(ctx); err == nil {
		if chunk != nil {
			glog.V(4).Infof("%s/%s flush existing [%d,%d)", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size))
			chunks = append(chunks, chunk)
		}
	} else {
		glog.V(0).Infof("%s/%s failed to flush1 [%d,%d): %v", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size), err)
		return
	}
	pages.Size = 0
	pages.Offset = 0

	// flush the new page
	if chunk, err = pages.saveToStorage(ctx, data, offset); err == nil {
		if chunk != nil {
			glog.V(4).Infof("%s/%s flush big request [%d,%d)", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size))
			chunks = append(chunks, chunk)
		}
	} else {
		glog.V(0).Infof("%s/%s failed to flush2 [%d,%d): %v", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size), err)
		return
	}

	return
}

func (pages *ContinuousDirtyPages) FlushToStorage(ctx context.Context) (chunk *filer_pb.FileChunk, err error) {

	pages.lock.Lock()
	defer pages.lock.Unlock()

	if pages.Size == 0 {
		return nil, nil
	}

	if chunk, err = pages.saveExistingPagesToStorage(ctx); err == nil {
		pages.Size = 0
		pages.Offset = 0
		if chunk != nil {
			glog.V(4).Infof("%s/%s flush [%d,%d)", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size))
		}
	}
	return
}

func (pages *ContinuousDirtyPages) saveExistingPagesToStorage(ctx context.Context) (*filer_pb.FileChunk, error) {

	if pages.Size == 0 {
		return nil, nil
	}

	return pages.saveToStorage(ctx, pages.Data[:pages.Size], pages.Offset)
}

func (pages *ContinuousDirtyPages) saveToStorage(ctx context.Context, buf []byte, offset int64) (*filer_pb.FileChunk, error) {

	var fileId, host string

	if err := pages.f.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.AssignVolumeRequest{
			Count:       1,
			Replication: pages.f.wfs.option.Replication,
			Collection:  pages.f.wfs.option.Collection,
			TtlSec:      pages.f.wfs.option.TtlSec,
			DataCenter:  pages.f.wfs.option.DataCenter,
		}

		resp, err := client.AssignVolume(ctx, request)
		if err != nil {
			glog.V(0).Infof("assign volume failure %v: %v", request, err)
			return err
		}

		fileId, host = resp.FileId, resp.Url

		return nil
	}); err != nil {
		return nil, fmt.Errorf("filerGrpcAddress assign volume: %v", err)
	}

	fileUrl := fmt.Sprintf("http://%s/%s", host, fileId)
	bufReader := bytes.NewReader(buf)
	uploadResult, err := operation.Upload(fileUrl, pages.f.Name, bufReader, false, "application/octet-stream", nil, "")
	if err != nil {
		glog.V(0).Infof("upload data %v to %s: %v", pages.f.Name, fileUrl, err)
		return nil, fmt.Errorf("upload data: %v", err)
	}
	if uploadResult.Error != "" {
		glog.V(0).Infof("upload failure %v to %s: %v", pages.f.Name, fileUrl, err)
		return nil, fmt.Errorf("upload result: %v", uploadResult.Error)
	}

	return &filer_pb.FileChunk{
		FileId: fileId,
		Offset: offset,
		Size:   uint64(len(buf)),
		Mtime:  time.Now().UnixNano(),
	}, nil

}

func max(x, y int64) int64 {
	if x > y {
		return x
	}
	return y
}
<|endoftext|>


<commit_before>package s3api

import (
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/pb/s3_pb"
	"github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
	"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
	"github.com/gorilla/mux"
	"net/http"
	"sync"
	"sync/atomic"
)

type CircuitBreaker struct {
	sync.RWMutex
	Enabled     bool
	counters    map[string]*int64
	limitations map[string]int64
}

func NewCircuitBreaker(option *S3ApiServerOption) *CircuitBreaker {
	cb := &CircuitBreaker{
		counters:    make(map[string]*int64),
		limitations: make(map[string]int64),
	}

	err := pb.WithFilerClient(false, option.Filer, option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
		content, err := filer.ReadInsideFiler(client, s3_constants.CircuitBreakerConfigDir, s3_constants.CircuitBreakerConfigFile)
		if err != nil {
			return fmt.Errorf("read S3 circuit breaker config: %v", err)
		}
		return cb.LoadS3ApiConfigurationFromBytes(content)
	})

	if err != nil {
		glog.Warningf("fail to load config: %v", err)
	}

	return cb
}

func (cb *CircuitBreaker) LoadS3ApiConfigurationFromBytes(content []byte) error {
	cbCfg := &s3_pb.S3CircuitBreakerConfig{}
	if err := filer.ParseS3ConfigurationFromBytes(content, cbCfg); err != nil {
		glog.Warningf("unmarshal error: %v", err)
		return fmt.Errorf("unmarshal error: %v", err)
	}
	if err := cb.loadCircuitBreakerConfig(cbCfg); err != nil {
		return err
	}
	return nil
}

func (cb *CircuitBreaker) loadCircuitBreakerConfig(cfg *s3_pb.S3CircuitBreakerConfig) error {

	//global
	globalEnabled := false
	globalOptions := cfg.Global
	limitations := make(map[string]int64)
	if globalOptions != nil && globalOptions.Enabled && len(globalOptions.Actions) > 0 {
		globalEnabled = globalOptions.Enabled
		for action, limit := range globalOptions.Actions {
			limitations[action] = limit
		}
	}
	cb.Enabled = globalEnabled

	//buckets
	for bucket, cbOptions := range cfg.Buckets {
		if cbOptions.Enabled {
			for action, limit := range cbOptions.Actions {
				limitations[s3_constants.Concat(bucket, action)] = limit
			}
		}
	}

	cb.limitations = limitations
	return nil
}

func (cb *CircuitBreaker) Limit(f func(w http.ResponseWriter, r *http.Request), action string) (http.HandlerFunc, Action) {
	return func(w http.ResponseWriter, r *http.Request) {
		if !cb.Enabled {
			f(w, r)
			return
		}

		vars := mux.Vars(r)
		bucket := vars["bucket"]

		rollback, errCode := cb.limit(r, bucket, action)
		defer func() {
			for _, rf := range rollback {
				rf()
			}
		}()

		if errCode == s3err.ErrNone {
			f(w, r)
			return
		}
		s3err.WriteErrorResponse(w, r, errCode)
	}, Action(action)
}

func (cb *CircuitBreaker) limit(r *http.Request, bucket string, action string) (rollback []func(), errCode s3err.ErrorCode) {

	//bucket simultaneous request count
	bucketCountRollBack, errCode := cb.loadCounterAndCompare(s3_constants.Concat(bucket, action, s3_constants.LimitTypeCount), 1, s3err.ErrTooManyRequest)
	if bucketCountRollBack != nil {
		rollback = append(rollback, bucketCountRollBack)
	}
	if errCode != s3err.ErrNone {
		return
	}

	//bucket simultaneous request content bytes
	bucketContentLengthRollBack, errCode := cb.loadCounterAndCompare(s3_constants.Concat(bucket, action, s3_constants.LimitTypeBytes), r.ContentLength, s3err.ErrRequestBytesExceed)
	if bucketContentLengthRollBack != nil {
		rollback = append(rollback, bucketContentLengthRollBack)
	}
	if errCode != s3err.ErrNone {
		return
	}

	//global simultaneous request count
	globalCountRollBack, errCode := cb.loadCounterAndCompare(s3_constants.Concat(action, s3_constants.LimitTypeCount), 1, s3err.ErrTooManyRequest)
	if globalCountRollBack != nil {
		rollback = append(rollback, globalCountRollBack)
	}
	if errCode != s3err.ErrNone {
		return
	}

	//global simultaneous request content bytes
	globalContentLengthRollBack, errCode := cb.loadCounterAndCompare(s3_constants.Concat(action, s3_constants.LimitTypeBytes), r.ContentLength, s3err.ErrRequestBytesExceed)
	if globalContentLengthRollBack != nil {
		rollback = append(rollback, globalContentLengthRollBack)
	}
	if errCode != s3err.ErrNone {
		return
	}
	return
}

func (cb *CircuitBreaker) loadCounterAndCompare(key string, inc int64, errCode s3err.ErrorCode) (f func(), e s3err.ErrorCode) {
	e = s3err.ErrNone
	if max, ok := cb.limitations[key]; ok {
		cb.RLock()
		counter, exists := cb.counters[key]
		cb.RUnlock()

		if !exists {
			cb.Lock()
			counter, exists = cb.counters[key]
			if !exists {
				var newCounter int64
				counter = &newCounter
				cb.counters[key] = counter
			}
			cb.Unlock()
		}
		current := atomic.LoadInt64(counter)
		if current+inc > max {
			e = errCode
			return
		} else {
			current := atomic.AddInt64(counter, inc)
			f = func() {
				atomic.AddInt64(counter, -inc)
			}
			if current > max {
				e = errCode
				return
			}
		}
	}
	return
}
<commit_msg>adjust log message<commit_after>package s3api

import (
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/pb/s3_pb"
	"github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
	"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
	"github.com/gorilla/mux"
	"net/http"
	"sync"
	"sync/atomic"
)

type CircuitBreaker struct {
	sync.RWMutex
	Enabled     bool
	counters    map[string]*int64
	limitations map[string]int64
}

func NewCircuitBreaker(option *S3ApiServerOption) *CircuitBreaker {
	cb := &CircuitBreaker{
		counters:    make(map[string]*int64),
		limitations: make(map[string]int64),
	}

	err := pb.WithFilerClient(false, option.Filer, option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
		content, err := filer.ReadInsideFiler(client, s3_constants.CircuitBreakerConfigDir, s3_constants.CircuitBreakerConfigFile)
		if err != nil {
			return fmt.Errorf("read S3 circuit breaker config: %v", err)
		}
		return cb.LoadS3ApiConfigurationFromBytes(content)
	})

	if err != nil {
		glog.Infof("s3 circuit breaker not configured: %v", err)
	}

	return cb
}

func (cb *CircuitBreaker) LoadS3ApiConfigurationFromBytes(content []byte) error {
	cbCfg := &s3_pb.S3CircuitBreakerConfig{}
	if err := filer.ParseS3ConfigurationFromBytes(content, cbCfg); err != nil {
		glog.Warningf("unmarshal error: %v", err)
		return fmt.Errorf("unmarshal error: %v", err)
	}
	if err := cb.loadCircuitBreakerConfig(cbCfg); err != nil {
		return err
	}
	return nil
}

func (cb *CircuitBreaker) loadCircuitBreakerConfig(cfg *s3_pb.S3CircuitBreakerConfig) error {

	//global
	globalEnabled := false
	globalOptions := cfg.Global
	limitations := make(map[string]int64)
	if globalOptions != nil && globalOptions.Enabled && len(globalOptions.Actions) > 0 {
		globalEnabled = globalOptions.Enabled
		for action, limit := range globalOptions.Actions {
			limitations[action] = limit
		}
	}
	cb.Enabled = globalEnabled

	//buckets
	for bucket, cbOptions := range cfg.Buckets {
		if cbOptions.Enabled {
			for action, limit := range cbOptions.Actions {
				limitations[s3_constants.Concat(bucket, action)] = limit
			}
		}
	}

	cb.limitations = limitations
	return nil
}

func (cb *CircuitBreaker) Limit(f func(w http.ResponseWriter, r *http.Request), action string) (http.HandlerFunc, Action) {
	return func(w http.ResponseWriter, r *http.Request) {
		if !cb.Enabled {
			f(w, r)
			return
		}

		vars := mux.Vars(r)
		bucket := vars["bucket"]

		rollback, errCode := cb.limit(r, bucket, action)
		defer func() {
			for _, rf := range rollback {
				rf()
			}
		}()

		if errCode == s3err.ErrNone {
			f(w, r)
			return
		}
		s3err.WriteErrorResponse(w, r, errCode)
	}, Action(action)
}

func (cb *CircuitBreaker) limit(r *http.Request, bucket string, action string) (rollback []func(), errCode s3err.ErrorCode) {

	//bucket simultaneous request count
	bucketCountRollBack, errCode := cb.loadCounterAndCompare(s3_constants.Concat(bucket, action, s3_constants.LimitTypeCount), 1, s3err.ErrTooManyRequest)
	if bucketCountRollBack != nil {
		rollback = append(rollback, bucketCountRollBack)
	}
	if errCode != s3err.ErrNone {
		return
	}

	//bucket simultaneous request content bytes
	bucketContentLengthRollBack, errCode := cb.loadCounterAndCompare(s3_constants.Concat(bucket, action, s3_constants.LimitTypeBytes), r.ContentLength, s3err.ErrRequestBytesExceed)
	if bucketContentLengthRollBack != nil {
		rollback = append(rollback, bucketContentLengthRollBack)
	}
	if errCode != s3err.ErrNone {
		return
	}

	//global simultaneous request count
	globalCountRollBack, errCode := cb.loadCounterAndCompare(s3_constants.Concat(action, s3_constants.LimitTypeCount), 1, s3err.ErrTooManyRequest)
	if globalCountRollBack != nil {
		rollback = append(rollback, globalCountRollBack)
	}
	if errCode != s3err.ErrNone {
		return
	}

	//global simultaneous request content bytes
	globalContentLengthRollBack, errCode := cb.loadCounterAndCompare(s3_constants.Concat(action, s3_constants.LimitTypeBytes), r.ContentLength, s3err.ErrRequestBytesExceed)
	if globalContentLengthRollBack != nil {
		rollback = append(rollback, globalContentLengthRollBack)
	}
	if errCode != s3err.ErrNone {
		return
	}
	return
}

func (cb *CircuitBreaker) loadCounterAndCompare(key string, inc int64, errCode s3err.ErrorCode) (f func(), e s3err.ErrorCode) {
	e = s3err.ErrNone
	if max, ok := cb.limitations[key]; ok {
		cb.RLock()
		counter, exists := cb.counters[key]
		cb.RUnlock()

		if !exists {
			cb.Lock()
			counter, exists = cb.counters[key]
			if !exists {
				var newCounter int64
				counter = &newCounter
				cb.counters[key] = counter
			}
			cb.Unlock()
		}
		current := atomic.LoadInt64(counter)
		if current+inc > max {
			e = errCode
			return
		} else {
			current := atomic.AddInt64(counter, inc)
			f = func() {
				atomic.AddInt64(counter, -inc)
			}
			if current > max {
				e = errCode
				return
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>package gcvgo

import (
	"bytes"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
)

// NewClient creates a new API client with the provided credentials
func NewClient(credentials Credentials) (Client, error) {

	return Client{
		httpClient:  &http.Client{},
		Credentials: credentials,
	}, nil
}

func (reqs *Requests) Add(request Request) {
	*reqs = append(*reqs, request)
}

func (request *Request) AddImageFromBase64(base64Content string) {
	request.Image.Content = base64Content
}

func (features *Features) Add(featureType FeatureType, MaxResults int64) {
	*features = append(*features, Feature{
		Type:       featureType,
		MaxResults: MaxResults,
	})
}

func (client *Client) Do(reqs Requests) ([]Response, error) {
	payload := struct {
		Requests Requests `json:"requests"`
	}{
		Requests: reqs,
	}
	JSONPayload, err := json.Marshal(payload)
	if err != nil {
		return []Response{}, err
	}
	fmt.Println(string(JSONPayload))

	response, _, err := client.fetchAndReturnPage(JSONPayload)
	if err != nil {
		return []Response{}, err
	}

	fmt.Println(string(response))

	var result struct {
		Responses []Response `json:"responses"`
	}
	err = json.Unmarshal(response, &result)
	if err != nil {
		return []Response{}, err
	}

	return result.Responses, nil
}

func (client *Client) fetchAndReturnPage(body []byte) ([]byte, http.Header, error) {

	domain := fmt.Sprintf("https://vision.googleapis.com/v1/images:annotate?key=%s", client.Credentials.APIkey)
	requestURL, err := url.Parse(domain)
	if err != nil {
		return []byte(""), http.Header{}, err
	}
	requestURL.Path = "/v1/images:annotate"

	fmt.Println(requestURL)

	request, err := http.NewRequest("POST", requestURL.String(), bytes.NewBuffer(body))
	if err != nil {
		return []byte(""), http.Header{}, fmt.Errorf("Failed to get the URL %s: %s", requestURL, err)
	}
	request.Header.Add("Content-Length", strconv.Itoa(len(body)))

	request.Header.Add("Connection", "Keep-Alive")
	request.Header.Add("Accept-Encoding", "gzip, deflate")
	request.Header.Add("Content-Type", "application/json")

	response, err := client.httpClient.Do(request)
	if err != nil {
		return []byte(""), http.Header{}, fmt.Errorf("Failed to get the URL %s: %s", requestURL, err)
	}
	defer response.Body.Close()

	var responseReader io.ReadCloser
	switch response.Header.Get("Content-Encoding") {
	case "gzip":
		decompressedBodyReader, err := gzip.NewReader(response.Body)
		if err != nil {
			return []byte(""), http.Header{}, err
		}
		responseReader = decompressedBodyReader
		defer responseReader.Close()
	default:
		responseReader = response.Body
	}

	responseBody, err := ioutil.ReadAll(responseReader)
	if err != nil {
		return []byte(""), http.Header{}, err
	}

	if response.StatusCode > 299 || response.StatusCode < 199 {
		var apiError Status
		err = json.Unmarshal(responseBody, &apiError)
		if err != nil {
			return []byte(""), http.Header{}, nil
		}
		fmt.Println(response.StatusCode)
		fmt.Println(string(responseBody))
		return responseBody, response.Header, fmt.Errorf("%s", responseBody)
	}

	return responseBody, response.Header, nil
}
<commit_msg>Comment debug prints<commit_after>package gcvgo

import (
	"bytes"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
)

// NewClient creates a new API client with the provided credentials
func NewClient(credentials Credentials) (Client, error) {

	return Client{
		httpClient:  &http.Client{},
		Credentials: credentials,
	}, nil
}

func (reqs *Requests) Add(request Request) {
	*reqs = append(*reqs, request)
}

func (request *Request) AddImageFromBase64(base64Content string) {
	request.Image.Content = base64Content
}

func (features *Features) Add(featureType FeatureType, MaxResults int64) {
	*features = append(*features, Feature{
		Type:       featureType,
		MaxResults: MaxResults,
	})
}

func (client *Client) Do(reqs Requests) ([]Response, error) {
	payload := struct {
		Requests Requests `json:"requests"`
	}{
		Requests: reqs,
	}
	JSONPayload, err := json.Marshal(payload)
	if err != nil {
		return []Response{}, err
	}
	//fmt.Println(string(JSONPayload))

	response, _, err := client.fetchAndReturnPage(JSONPayload)
	if err != nil {
		return []Response{}, err
	}

	//fmt.Println(string(response))

	var result struct {
		Responses []Response `json:"responses"`
	}
	err = json.Unmarshal(response, &result)
	if err != nil {
		return []Response{}, err
	}

	return result.Responses, nil
}

func (client *Client) fetchAndReturnPage(body []byte) ([]byte, http.Header, error) {

	domain := fmt.Sprintf("https://vision.googleapis.com/v1/images:annotate?key=%s", client.Credentials.APIkey)
	requestURL, err := url.Parse(domain)
	if err != nil {
		return []byte(""), http.Header{}, err
	}
	requestURL.Path = "/v1/images:annotate"

	//fmt.Println(requestURL)

	request, err := http.NewRequest("POST", requestURL.String(), bytes.NewBuffer(body))
	if err != nil {
		return []byte(""), http.Header{}, fmt.Errorf("Failed to get the URL %s: %s", requestURL, err)
	}
	request.Header.Add("Content-Length", strconv.Itoa(len(body)))

	request.Header.Add("Connection", "Keep-Alive")
	request.Header.Add("Accept-Encoding", "gzip, deflate")
	request.Header.Add("Content-Type", "application/json")

	response, err := client.httpClient.Do(request)
	if err != nil {
		return []byte(""), http.Header{}, fmt.Errorf("Failed to get the URL %s: %s", requestURL, err)
	}
	defer response.Body.Close()

	var responseReader io.ReadCloser
	switch response.Header.Get("Content-Encoding") {
	case "gzip":
		decompressedBodyReader, err := gzip.NewReader(response.Body)
		if err != nil {
			return []byte(""), http.Header{}, err
		}
		responseReader = decompressedBodyReader
		defer responseReader.Close()
	default:
		responseReader = response.Body
	}

	responseBody, err := ioutil.ReadAll(responseReader)
	if err != nil {
		return []byte(""), http.Header{}, err
	}

	if response.StatusCode > 299 || response.StatusCode < 199 {
		var apiError Status
		err = json.Unmarshal(responseBody, &apiError)
		if err != nil {
			return []byte(""), http.Header{}, nil
		}
		fmt.Println(response.StatusCode)
		fmt.Println(string(responseBody))
		return responseBody, response.Header, fmt.Errorf("%s", responseBody)
	}

	return responseBody, response.Header, nil
}
<|endoftext|>


<commit_before>// +build !novault

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

package secret

// Functions for using hashicorp vault (https://www.vaultproject.io/) to store
// secrets in goiardi.

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"github.com/ctdk/goiardi/config"
	vault "github.com/hashicorp/vault/api"
	"github.com/tideland/golib/logger"
	"sync"
	"time"
)

// make this a pool later?

type vaultSecretStore struct {
	m       sync.RWMutex
	secrets map[string]*secretVal
	*vault.Client
}

const MaxStaleAgeSeconds = 3600 // configurable later, but make it an hour for
// now
const StaleTryAgainSeconds = 60 // try stale values again in a minute

type secretVal struct {
	path          string
	secretType    string
	created       time.Time
	renewable     bool
	ttl           time.Duration
	expires       time.Time
	stale         bool
	staleTryAgain time.Time
	staleTime     time.Time
	value         interface{}
}

type secretConvert func(interface{}) (interface{}, error)

func configureVault() (*vaultSecretStore, error) {
	conf := vault.DefaultConfig()
	if err := conf.ReadEnvironment(); err != nil {
		return nil, err
	}
	if config.Config.VaultAddr != "" {
		conf.Address = config.Config.VaultAddr
	}
	c, err := vault.NewClient(conf)
	if err != nil {
		return nil, err
	}

	var m sync.RWMutex
	secrets := make(map[string]*secretVal)
	v := &vaultSecretStore{m, secrets, c}
	return v, nil
}

func (v *vaultSecretStore) getSecret(path string, secretType string) (interface{}, error) {
	if v.secrets[path] == nil {
		logger.Debugf("secret (%s) for %s is nil, fetching from vault", secretType, path)
		s, err := v.getSecretPath(path, secretType)
		if err != nil {
			return "", err
		}
		v.secrets[path] = s
	} else {
		logger.Debugf("using cached secret for %s", path)
	}
	return v.secretValue(v.secrets[path])
}

func (v *vaultSecretStore) getSecretPath(path string, secretType string) (*secretVal, error) {
	t := time.Now()
	s, err := v.Logical().Read(path)
	if err != nil {
		err := fmt.Errorf("Failed to read %s (%s) from vault: %s", path, secretType, err.Error())
		return nil, err
	}
	if s == nil {
		err := fmt.Errorf("No secret returned from vault for %s (%s)", path, secretType)
		return nil, err
	}
	p := s.Data[secretType]
	if p == nil {
		err := fmt.Errorf("no data for %s (%s) from vault", path, secretType)
		return nil, err
	}
	p, err = convertors(secretType)(p)
	if err != nil {
		return nil, err
	}
	sVal := newSecretVal(path, secretType, p, t, s)
	return sVal, nil
}

func (v *vaultSecretStore) setSecret(path string, secretType string, value interface{}) error {
	logger.Debugf("setting public key for %s (%s)", path, secretType)
	t := time.Now()
	_, err := v.Logical().Write(path, map[string]interface{}{
		secretType: value,
	})
	if err != nil {
		return err
	}
	s, err := v.Logical().Read(path)
	if err != nil {
		return fmt.Errorf("Error re-reading secret from vault after setting: %s", err.Error())
	}
	sVal := newSecretVal(path, secretType, value, t, s)
	v.secrets[path] = sVal
	return nil
}

func (v *vaultSecretStore) deleteSecret(path string) error {
	delete(v.secrets, path)
	_, err := v.Logical().Delete(path)
	if err != nil {
		return err
	}
	return nil
}

func (v *vaultSecretStore) getPublicKey(c ActorKeyer) (string, error) {
	v.m.RLock()
	defer v.m.RUnlock()
	path := makePubKeyPath(c)
	s, err := v.getSecret(path, "pubKey")
	switch s := s.(type) {
	case string:
		return s, err
	case []byte:
		return string(s), err
	case nil:
		return "", err
	default:
		var errStr string
		if err != nil {
			errStr = err.Error()
		}
		err := fmt.Errorf("The type was wrong fetching the public key from vault: %T -- error, if any: %s", s, errStr)
		return "", err
	}
}

func (v *vaultSecretStore) setPublicKey(c ActorKeyer, pubKey string) error {
	v.m.Lock()
	defer v.m.Unlock()
	path := makePubKeyPath(c)
	return v.setSecret(path, "pubKey", pubKey)
}

func (v *vaultSecretStore) deletePublicKey(c ActorKeyer) error {
	v.m.Lock()
	defer v.m.Unlock()
	path := makePubKeyPath(c)
	return v.deleteSecret(path)
}

func makePubKeyPath(c ActorKeyer) string {
	return fmt.Sprintf("keys/%s/%s", c.URLType(), c.GetName())
}

func makeHashPath(c ActorKeyer) string {
	// strictly speaking only users actually have passwords, but in case
	// something else ever comes up, make the path a little longer.
	return fmt.Sprintf("keys/passwd/%s/%s", c.URLType(), c.GetName())
}

func newSecretVal(path string, secretType string, value interface{}, t time.Time, s *vault.Secret) *secretVal {
	sVal := new(secretVal)
	sVal.path = path
	sVal.secretType = secretType
	sVal.created = t
	sVal.renewable = s.Renewable
	sVal.ttl = time.Duration(s.LeaseDuration) * time.Second
	sVal.expires = t.Add(sVal.ttl)
	sVal.value = value
	return sVal
}

func (s *secretVal) isExpired() bool {
	if s.ttl == 0 {
		return false
	}
	return time.Now().After(s.expires)
}

func (v *vaultSecretStore) secretValue(s *secretVal) (interface{}, error) {
	if s.isExpired() {
		logger.Debugf("trying to renew secret for %s", s.path)
		s2, err := v.getSecretPath(s.path, s.secretType)
		if !s.stale {
			if err != nil {
				logger.Debugf("error trying to renew the secret for %s: %s -- marking as stale", s.path, err.Error())
				s.stale = true
				s.staleTime = time.Now().Add(MaxStaleAgeSeconds * time.Second)
				s.staleTryAgain = time.Now().Add(StaleTryAgainSeconds * time.Second)
			} else {
				logger.Debugf("successfully renewed secret for %s", s.path)
				s = s2
			}
		} else if time.Now().After(s.staleTime) {
			if err != nil {
				err := fmt.Errorf("Couldn't renew the secret for %s before %d seconds ran out, giving up", s.path, MaxStaleAgeSeconds)
				return nil, err
			}
			logger.Debugf("successfully renewed secret for %s beforegiving up due to staleness", s.path)
			s = s2
		} else if time.Now().After(s.staleTryAgain) {
			if err != nil {
				logger.Debugf("error trying to renew the secret for %s: %s -- will renew again in %d seconds", s.path, err.Error(), StaleTryAgainSeconds)
				s.staleTryAgain = time.Now().Add(StaleTryAgainSeconds)
			} else {
				logger.Debugf("successfully renewed secret after being stale")
				s = s2
			}
		}
	}
	return s.value, nil
}

func (v *vaultSecretStore) valueStr(s *secretVal) (string, error) {
	val, err := v.secretValue(s)
	if err != nil {
		return "", err
	}
	valStr, ok := val.(string)
	if !ok {
		err := fmt.Errorf("value for %s was not a string, but a %T!", s.path, val)
		return "", err
	}
	return valStr, nil
}

// shovey signing key

func (v *vaultSecretStore) getSigningKey(path string) (*rsa.PrivateKey, error) {
	v.m.RLock()
	defer v.m.RUnlock()
	s, err := v.getSecret(path, "RSAKey")
	switch s := s.(type) {
	case *rsa.PrivateKey:
		return s, err
	default:
		var errStr string
		if err != nil {
			errStr = err.Error()
		}
		kerr := fmt.Errorf("RSA private key for shovey was not returned. An object of type %T was. Error, if any: %s", s, errStr)
		return nil, kerr
	}
}

// user passwd hash methods

func (v *vaultSecretStore) setPasswdHash(c ActorKeyer, pwhash string) error {
	v.m.Lock()
	defer v.m.Unlock()
	path := makeHashPath(c)
	return v.setSecret(path, "passwd", pwhash)
}

func (v *vaultSecretStore) getPasswdHash(c ActorKeyer) (string, error) {
	v.m.RLock()
	defer v.m.RUnlock()
	path := makeHashPath(c)
	s, err := v.getSecret(path, "passwd")
	switch s := s.(type) {
	case string:
		return s, err
	case []byte:
		return string(s), err
	case nil:
		return "", err
	default:
		var errStr string
		if err != nil {
			errStr = err.Error()
		}
		err := fmt.Errorf("The type was wrong fetching the passwd hash from vault: %T -- error, if any: %s", s, errStr)
		return "", err
	}
}

func (v *vaultSecretStore) deletePasswdHash(c ActorKeyer) error {
	v.m.Lock()
	defer v.m.Unlock()
	path := makeHashPath(c)
	return v.deleteSecret(path)
}

// funcs to process secrets after fetching them from vault

func secretPassThrough(i interface{}) (interface{}, error) {
	return i, nil
}

func secretRSAKey(i interface{}) (interface{}, error) {
	p, ok := i.(string)
	if !ok {
		return nil, fmt.Errorf("not an RSA private key in string form")
	}
	pBlock, _ := pem.Decode([]byte(p))
	if pBlock == nil {
		return nil, fmt.Errorf("invalid block size for private key for shovey from vault")
	}
	pk, err := x509.ParsePKCS1PrivateKey(pBlock.Bytes)
	if err != nil {
		return nil, err
	}
	return pk, nil
}

func convertors(secretType string) secretConvert {
	switch secretType {
	case "RSAKey":
		return secretRSAKey
	default:
		return secretPassThrough
	}
}
<commit_msg>fix go vet error in secrets processing<commit_after>// +build !novault

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

package secret

// Functions for using hashicorp vault (https://www.vaultproject.io/) to store
// secrets in goiardi.

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"github.com/ctdk/goiardi/config"
	vault "github.com/hashicorp/vault/api"
	"github.com/tideland/golib/logger"
	"sync"
	"time"
)

// make this a pool later?

type vaultSecretStore struct {
	m       sync.RWMutex
	secrets map[string]*secretVal
	*vault.Client
}

const MaxStaleAgeSeconds = 3600 // configurable later, but make it an hour for
// now
const StaleTryAgainSeconds = 60 // try stale values again in a minute

type secretVal struct {
	path          string
	secretType    string
	created       time.Time
	renewable     bool
	ttl           time.Duration
	expires       time.Time
	stale         bool
	staleTryAgain time.Time
	staleTime     time.Time
	value         interface{}
}

type secretConvert func(interface{}) (interface{}, error)

func configureVault() (*vaultSecretStore, error) {
	conf := vault.DefaultConfig()
	if err := conf.ReadEnvironment(); err != nil {
		return nil, err
	}
	if config.Config.VaultAddr != "" {
		conf.Address = config.Config.VaultAddr
	}
	c, err := vault.NewClient(conf)
	if err != nil {
		return nil, err
	}

	secrets := make(map[string]*secretVal)
	v := &vaultSecretStore{secrets: secrets, Client: c}
	return v, nil
}

func (v *vaultSecretStore) getSecret(path string, secretType string) (interface{}, error) {
	if v.secrets[path] == nil {
		logger.Debugf("secret (%s) for %s is nil, fetching from vault", secretType, path)
		s, err := v.getSecretPath(path, secretType)
		if err != nil {
			return "", err
		}
		v.secrets[path] = s
	} else {
		logger.Debugf("using cached secret for %s", path)
	}
	return v.secretValue(v.secrets[path])
}

func (v *vaultSecretStore) getSecretPath(path string, secretType string) (*secretVal, error) {
	t := time.Now()
	s, err := v.Logical().Read(path)
	if err != nil {
		err := fmt.Errorf("Failed to read %s (%s) from vault: %s", path, secretType, err.Error())
		return nil, err
	}
	if s == nil {
		err := fmt.Errorf("No secret returned from vault for %s (%s)", path, secretType)
		return nil, err
	}
	p := s.Data[secretType]
	if p == nil {
		err := fmt.Errorf("no data for %s (%s) from vault", path, secretType)
		return nil, err
	}
	p, err = convertors(secretType)(p)
	if err != nil {
		return nil, err
	}
	sVal := newSecretVal(path, secretType, p, t, s)
	return sVal, nil
}

func (v *vaultSecretStore) setSecret(path string, secretType string, value interface{}) error {
	logger.Debugf("setting public key for %s (%s)", path, secretType)
	t := time.Now()
	_, err := v.Logical().Write(path, map[string]interface{}{
		secretType: value,
	})
	if err != nil {
		return err
	}
	s, err := v.Logical().Read(path)
	if err != nil {
		return fmt.Errorf("Error re-reading secret from vault after setting: %s", err.Error())
	}
	sVal := newSecretVal(path, secretType, value, t, s)
	v.secrets[path] = sVal
	return nil
}

func (v *vaultSecretStore) deleteSecret(path string) error {
	delete(v.secrets, path)
	_, err := v.Logical().Delete(path)
	if err != nil {
		return err
	}
	return nil
}

func (v *vaultSecretStore) getPublicKey(c ActorKeyer) (string, error) {
	v.m.RLock()
	defer v.m.RUnlock()
	path := makePubKeyPath(c)
	s, err := v.getSecret(path, "pubKey")
	switch s := s.(type) {
	case string:
		return s, err
	case []byte:
		return string(s), err
	case nil:
		return "", err
	default:
		var errStr string
		if err != nil {
			errStr = err.Error()
		}
		err := fmt.Errorf("The type was wrong fetching the public key from vault: %T -- error, if any: %s", s, errStr)
		return "", err
	}
}

func (v *vaultSecretStore) setPublicKey(c ActorKeyer, pubKey string) error {
	v.m.Lock()
	defer v.m.Unlock()
	path := makePubKeyPath(c)
	return v.setSecret(path, "pubKey", pubKey)
}

func (v *vaultSecretStore) deletePublicKey(c ActorKeyer) error {
	v.m.Lock()
	defer v.m.Unlock()
	path := makePubKeyPath(c)
	return v.deleteSecret(path)
}

func makePubKeyPath(c ActorKeyer) string {
	return fmt.Sprintf("keys/%s/%s", c.URLType(), c.GetName())
}

func makeHashPath(c ActorKeyer) string {
	// strictly speaking only users actually have passwords, but in case
	// something else ever comes up, make the path a little longer.
	return fmt.Sprintf("keys/passwd/%s/%s", c.URLType(), c.GetName())
}

func newSecretVal(path string, secretType string, value interface{}, t time.Time, s *vault.Secret) *secretVal {
	sVal := new(secretVal)
	sVal.path = path
	sVal.secretType = secretType
	sVal.created = t
	sVal.renewable = s.Renewable
	sVal.ttl = time.Duration(s.LeaseDuration) * time.Second
	sVal.expires = t.Add(sVal.ttl)
	sVal.value = value
	return sVal
}

func (s *secretVal) isExpired() bool {
	if s.ttl == 0 {
		return false
	}
	return time.Now().After(s.expires)
}

func (v *vaultSecretStore) secretValue(s *secretVal) (interface{}, error) {
	if s.isExpired() {
		logger.Debugf("trying to renew secret for %s", s.path)
		s2, err := v.getSecretPath(s.path, s.secretType)
		if !s.stale {
			if err != nil {
				logger.Debugf("error trying to renew the secret for %s: %s -- marking as stale", s.path, err.Error())
				s.stale = true
				s.staleTime = time.Now().Add(MaxStaleAgeSeconds * time.Second)
				s.staleTryAgain = time.Now().Add(StaleTryAgainSeconds * time.Second)
			} else {
				logger.Debugf("successfully renewed secret for %s", s.path)
				s = s2
			}
		} else if time.Now().After(s.staleTime) {
			if err != nil {
				err := fmt.Errorf("Couldn't renew the secret for %s before %d seconds ran out, giving up", s.path, MaxStaleAgeSeconds)
				return nil, err
			}
			logger.Debugf("successfully renewed secret for %s beforegiving up due to staleness", s.path)
			s = s2
		} else if time.Now().After(s.staleTryAgain) {
			if err != nil {
				logger.Debugf("error trying to renew the secret for %s: %s -- will renew again in %d seconds", s.path, err.Error(), StaleTryAgainSeconds)
				s.staleTryAgain = time.Now().Add(StaleTryAgainSeconds)
			} else {
				logger.Debugf("successfully renewed secret after being stale")
				s = s2
			}
		}
	}
	return s.value, nil
}

func (v *vaultSecretStore) valueStr(s *secretVal) (string, error) {
	val, err := v.secretValue(s)
	if err != nil {
		return "", err
	}
	valStr, ok := val.(string)
	if !ok {
		err := fmt.Errorf("value for %s was not a string, but a %T!", s.path, val)
		return "", err
	}
	return valStr, nil
}

// shovey signing key

func (v *vaultSecretStore) getSigningKey(path string) (*rsa.PrivateKey, error) {
	v.m.RLock()
	defer v.m.RUnlock()
	s, err := v.getSecret(path, "RSAKey")
	switch s := s.(type) {
	case *rsa.PrivateKey:
		return s, err
	default:
		var errStr string
		if err != nil {
			errStr = err.Error()
		}
		kerr := fmt.Errorf("RSA private key for shovey was not returned. An object of type %T was. Error, if any: %s", s, errStr)
		return nil, kerr
	}
}

// user passwd hash methods

func (v *vaultSecretStore) setPasswdHash(c ActorKeyer, pwhash string) error {
	v.m.Lock()
	defer v.m.Unlock()
	path := makeHashPath(c)
	return v.setSecret(path, "passwd", pwhash)
}

func (v *vaultSecretStore) getPasswdHash(c ActorKeyer) (string, error) {
	v.m.RLock()
	defer v.m.RUnlock()
	path := makeHashPath(c)
	s, err := v.getSecret(path, "passwd")
	switch s := s.(type) {
	case string:
		return s, err
	case []byte:
		return string(s), err
	case nil:
		return "", err
	default:
		var errStr string
		if err != nil {
			errStr = err.Error()
		}
		err := fmt.Errorf("The type was wrong fetching the passwd hash from vault: %T -- error, if any: %s", s, errStr)
		return "", err
	}
}

func (v *vaultSecretStore) deletePasswdHash(c ActorKeyer) error {
	v.m.Lock()
	defer v.m.Unlock()
	path := makeHashPath(c)
	return v.deleteSecret(path)
}

// funcs to process secrets after fetching them from vault

func secretPassThrough(i interface{}) (interface{}, error) {
	return i, nil
}

func secretRSAKey(i interface{}) (interface{}, error) {
	p, ok := i.(string)
	if !ok {
		return nil, fmt.Errorf("not an RSA private key in string form")
	}
	pBlock, _ := pem.Decode([]byte(p))
	if pBlock == nil {
		return nil, fmt.Errorf("invalid block size for private key for shovey from vault")
	}
	pk, err := x509.ParsePKCS1PrivateKey(pBlock.Bytes)
	if err != nil {
		return nil, err
	}
	return pk, nil
}

func convertors(secretType string) secretConvert {
	switch secretType {
	case "RSAKey":
		return secretRSAKey
	default:
		return secretPassThrough
	}
}
<|endoftext|>


<commit_before>package godbg

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"regexp"
	"runtime/debug"
	"strings"
)

var rxDbgLine, _ = regexp.Compile(`^.*[Vv]on[Cc](?:/prog/git)?/senvgo/main.go:(\d+)\s`)
var rxDbgFnct, _ = regexp.Compile(`^\s+(?:com/VonC/senvgo)?(?:\.\(([^\)]+)\))?\.?([^:]+)`)

// http://stackoverflow.com/a/23554672/6309 https://vividcortex.com/blog/2013/12/03/go-idiom-package-and-object/
// you design a type with methods as usual, and then you also place matching functions at the package level itself.
// These functions simply delegate to a default instance of the type that’s a private package-level variable, created in an init() function.

// Pdbg allows to print debug message with indent and function name added
type Pdbg struct {
	bout *bytes.Buffer
	berr *bytes.Buffer
	sout *bufio.Writer
	serr *bufio.Writer
}

// Out returns a writer for normal messages.
// By default, os.StdOut
func Out() io.Writer {
	return pdbg.Out()
}

// Out returns a writer for normal messages for a given pdbg instance.
// By default, os.StdOut
func (pdbg *Pdbg) Out() io.Writer {
	if pdbg.sout == nil {
		return os.Stdout
	}
	return pdbg.sout
}

// Err returns a writer for error messages.
// By default, os.StdErr
func Err() io.Writer {
	return pdbg.Err()
}

// Err returns a writer for error messages for a given pdbg instance.
// By default, os.StdErr
func (pdbg *Pdbg) Err() io.Writer {
	if pdbg.serr == nil {
		return os.Stderr
	}
	return pdbg.serr
}

// global pdbg used for printing
var pdbg = &Pdbg{}

// Option set an option for a Pdbg
// http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
type Option func(*Pdbg)

// SetBuffers is an option for replacing stdout and stderr by
// bytes buffers (in a bufio.Writer).
// If apdbg is nil, set for the global pdbg instance
func SetBuffers(apdbg *Pdbg) {
	if apdbg == nil {
		apdbg = pdbg
	}
	apdbg.bout = bytes.NewBuffer(nil)
	apdbg.sout = bufio.NewWriter(apdbg.bout)
	apdbg.berr = bytes.NewBuffer(nil)
	apdbg.serr = bufio.NewWriter(apdbg.berr)
}

// NewPdbg creates a PDbg instance, with options
func NewPdbg(options ...Option) *Pdbg {
	newpdbg := &Pdbg{}
	for _, option := range options {
		option(newpdbg)
	}
	return newpdbg
}

// ResetIOs reset the out and err buffer
// (unless they were the default stdout and stderr,
// in which case it does nothing)
func (pdbg *Pdbg) ResetIOs() {
	if pdbg.sout != nil {
		pdbg.bout = bytes.NewBuffer(nil)
		pdbg.sout.Reset(pdbg.bout)
		pdbg.berr = bytes.NewBuffer(nil)
		pdbg.serr.Reset(pdbg.berr)
	}
}

// FlushIOs flushes the sout and serr bufio.Writer
func (pdbg *Pdbg) FlushIOs() {
	pdbg.sout.Flush()
	pdbg.serr.Flush()
}

func pdbgInc(scanner *bufio.Scanner, line string) string {
	m := rxDbgLine.FindSubmatchIndex([]byte(line))
	if len(m) == 0 {
		return ""
	}
	dbgLine := line[m[2]:m[3]]
	// fmt.Printf("line '%v', m '%+v'\n", line, m)
	scanner.Scan()
	line = scanner.Text()
	mf := rxDbgFnct.FindSubmatchIndex([]byte(line))
	// fmt.Printf("lineF '%v', mf '%+v'\n", line, mf)
	if len(mf) == 0 {
		return ""
	}
	dbgFnct := ""
	if mf[2] > -1 {
		dbgFnct = line[mf[2]:mf[3]]
	}
	if dbgFnct != "" {
		dbgFnct = dbgFnct + "."
	}
	dbgFnct = dbgFnct + line[mf[4]:mf[5]]

	return dbgFnct + ":" + dbgLine
}

func pdbgExcluded(dbg string) bool {
	if strings.Contains(dbg, "ReadConfig:") {
		return false
	}
	return false
}

// Pdbgf uses global Pdbg variable for printing strings, with indent and function name
func Pdbgf(format string, args ...interface{}) string {
	msg := fmt.Sprintf(format+"\n", args...)
	msg = strings.TrimSpace(msg)
	bstack := bytes.NewBuffer(debug.Stack())
	// fmt.Printf("%+v", bstack)

	scanner := bufio.NewScanner(bstack)
	pmsg := ""
	depth := 0
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Contains(line, "smartystreets") {
			break
		}
		m := rxDbgLine.FindSubmatchIndex([]byte(line))
		if len(m) == 0 {
			continue
		}
		if depth > 0 && depth < 4 {
			dbg := pdbgInc(scanner, line)
			if dbg == "" {
				continue
			}
			if depth == 1 {
				if pdbgExcluded(dbg) {
					return ""
				}
				pmsg = "[" + dbg + "]"
			} else {
				pmsg = pmsg + " (" + dbg + ")"
			}
		}
		depth = depth + 1
	}
	spaces := ""
	if depth >= 2 {
		spaces = strings.Repeat(" ", depth-2)
	}
	res := pmsg
	pmsg = spaces + pmsg
	msg = pmsg + "\n" + spaces + "  " + msg + "\n"
	// fmt.Printf("MSG '%v'\n", msg)
	fmt.Fprint(os.Stderr, fmt.Sprint(msg))
	return res
}
<commit_msg>Removes FlushIOs, will be done in String() methods<commit_after>package godbg

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"regexp"
	"runtime/debug"
	"strings"
)

var rxDbgLine, _ = regexp.Compile(`^.*[Vv]on[Cc](?:/prog/git)?/senvgo/main.go:(\d+)\s`)
var rxDbgFnct, _ = regexp.Compile(`^\s+(?:com/VonC/senvgo)?(?:\.\(([^\)]+)\))?\.?([^:]+)`)

// http://stackoverflow.com/a/23554672/6309 https://vividcortex.com/blog/2013/12/03/go-idiom-package-and-object/
// you design a type with methods as usual, and then you also place matching functions at the package level itself.
// These functions simply delegate to a default instance of the type that’s a private package-level variable, created in an init() function.

// Pdbg allows to print debug message with indent and function name added
type Pdbg struct {
	bout *bytes.Buffer
	berr *bytes.Buffer
	sout *bufio.Writer
	serr *bufio.Writer
}

// Out returns a writer for normal messages.
// By default, os.StdOut
func Out() io.Writer {
	return pdbg.Out()
}

// Out returns a writer for normal messages for a given pdbg instance.
// By default, os.StdOut
func (pdbg *Pdbg) Out() io.Writer {
	if pdbg.sout == nil {
		return os.Stdout
	}
	return pdbg.sout
}

// Err returns a writer for error messages.
// By default, os.StdErr
func Err() io.Writer {
	return pdbg.Err()
}

// Err returns a writer for error messages for a given pdbg instance.
// By default, os.StdErr
func (pdbg *Pdbg) Err() io.Writer {
	if pdbg.serr == nil {
		return os.Stderr
	}
	return pdbg.serr
}

// global pdbg used for printing
var pdbg = &Pdbg{}

// Option set an option for a Pdbg
// http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
type Option func(*Pdbg)

// SetBuffers is an option for replacing stdout and stderr by
// bytes buffers (in a bufio.Writer).
// If apdbg is nil, set for the global pdbg instance
func SetBuffers(apdbg *Pdbg) {
	if apdbg == nil {
		apdbg = pdbg
	}
	apdbg.bout = bytes.NewBuffer(nil)
	apdbg.sout = bufio.NewWriter(apdbg.bout)
	apdbg.berr = bytes.NewBuffer(nil)
	apdbg.serr = bufio.NewWriter(apdbg.berr)
}

// NewPdbg creates a PDbg instance, with options
func NewPdbg(options ...Option) *Pdbg {
	newpdbg := &Pdbg{}
	for _, option := range options {
		option(newpdbg)
	}
	return newpdbg
}

// ResetIOs reset the out and err buffer
// (unless they were the default stdout and stderr,
// in which case it does nothing)
func (pdbg *Pdbg) ResetIOs() {
	if pdbg.sout != nil {
		pdbg.bout = bytes.NewBuffer(nil)
		pdbg.sout.Reset(pdbg.bout)
		pdbg.berr = bytes.NewBuffer(nil)
		pdbg.serr.Reset(pdbg.berr)
	}
}

func pdbgInc(scanner *bufio.Scanner, line string) string {
	m := rxDbgLine.FindSubmatchIndex([]byte(line))
	if len(m) == 0 {
		return ""
	}
	dbgLine := line[m[2]:m[3]]
	// fmt.Printf("line '%v', m '%+v'\n", line, m)
	scanner.Scan()
	line = scanner.Text()
	mf := rxDbgFnct.FindSubmatchIndex([]byte(line))
	// fmt.Printf("lineF '%v', mf '%+v'\n", line, mf)
	if len(mf) == 0 {
		return ""
	}
	dbgFnct := ""
	if mf[2] > -1 {
		dbgFnct = line[mf[2]:mf[3]]
	}
	if dbgFnct != "" {
		dbgFnct = dbgFnct + "."
	}
	dbgFnct = dbgFnct + line[mf[4]:mf[5]]

	return dbgFnct + ":" + dbgLine
}

func pdbgExcluded(dbg string) bool {
	if strings.Contains(dbg, "ReadConfig:") {
		return false
	}
	return false
}

// Pdbgf uses global Pdbg variable for printing strings, with indent and function name
func Pdbgf(format string, args ...interface{}) string {
	msg := fmt.Sprintf(format+"\n", args...)
	msg = strings.TrimSpace(msg)
	bstack := bytes.NewBuffer(debug.Stack())
	// fmt.Printf("%+v", bstack)

	scanner := bufio.NewScanner(bstack)
	pmsg := ""
	depth := 0
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Contains(line, "smartystreets") {
			break
		}
		m := rxDbgLine.FindSubmatchIndex([]byte(line))
		if len(m) == 0 {
			continue
		}
		if depth > 0 && depth < 4 {
			dbg := pdbgInc(scanner, line)
			if dbg == "" {
				continue
			}
			if depth == 1 {
				if pdbgExcluded(dbg) {
					return ""
				}
				pmsg = "[" + dbg + "]"
			} else {
				pmsg = pmsg + " (" + dbg + ")"
			}
		}
		depth = depth + 1
	}
	spaces := ""
	if depth >= 2 {
		spaces = strings.Repeat(" ", depth-2)
	}
	res := pmsg
	pmsg = spaces + pmsg
	msg = pmsg + "\n" + spaces + "  " + msg + "\n"
	// fmt.Printf("MSG '%v'\n", msg)
	fmt.Fprint(os.Stderr, fmt.Sprint(msg))
	return res
}
<|endoftext|>


<commit_before>// Package gorse holds functions common to the different tools making up the
// project.
package gorse

import (
	"database/sql"
	"fmt"
	"time"
)

// ReadState holds an item's state (rss_item_state table, read_state type).
type ReadState int

const (
	// Unread means the item is not yet read.
	Unread ReadState = iota
	// Read means the item was read.
	Read
	// ReadLater means to save the item to read later.
	ReadLater
)

// DBItem represents an item in the database.
type DBItem struct {
	ID              int64
	Title           string
	Description     string
	Link            string
	RSSFeedID       int64
	PublicationDate time.Time
	GUID            *string
}

// ErrItemNotFound means the item was not found in the database.
var ErrItemNotFound = fmt.Errorf("item not found")

// DBSetItemReadState sets the item's read state for the user.
func DBSetItemReadState(db *sql.DB, id int64, userID int,
	state ReadState) error {
	// Upsert.
	query := `
INSERT INTO rss_item_state
(user_id, item_id, state)
VALUES($1, $2, $3)
ON CONFLICT (user_id, item_id) DO UPDATE
SET state = $4
`
	_, err := db.Exec(query, userID, id, state.String(), state.String())
	if err != nil {
		return fmt.Errorf("unable to set read state on item: %d: %s", id, err)
	}

	return nil
}

// Turn read state into the enumerated type in the database (read_state).
func (s ReadState) String() string {
	if s == Unread {
		return "unread"
	}
	if s == Read {
		return "read"
	}
	return "read-later"
}

// FindItemByLink retrieves an item's information from the database by feed and
// link. Link is unique per feed.
func FindItemByLink(db *sql.DB, feedID int64, link string) (*DBItem, error) {
	query := `
SELECT
id, title, description, link, rss_feed_id, publication_date, guid
FROM rss_item
WHERE rss_feed_id = $1 AND
link = $2
`

	rows, err := db.Query(query, feedID, link)
	if err != nil {
		return nil, err
	}

	for rows.Next() {
		item := &DBItem{}

		if err := rows.Scan(&item.ID, &item.Title, &item.Description, &item.Link,
			&item.RSSFeedID, &item.PublicationDate, &item.GUID); err != nil {
			_ = rows.Close()
			return nil, fmt.Errorf("failed to scan row: %s", err)
		}

		if err := rows.Close(); err != nil {
			return nil, fmt.Errorf("error closing rows: %s", err)
		}

		return item, nil
	}

	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("failure fetching rows: %s", err)
	}

	return nil, ErrItemNotFound
}
<commit_msg>Do not loop when we expect one row<commit_after>// Package gorse holds functions common to the different tools making up the
// project.
package gorse

import (
	"database/sql"
	"fmt"
	"time"
)

// ReadState holds an item's state (rss_item_state table, read_state type).
type ReadState int

const (
	// Unread means the item is not yet read.
	Unread ReadState = iota
	// Read means the item was read.
	Read
	// ReadLater means to save the item to read later.
	ReadLater
)

// DBItem represents an item in the database.
type DBItem struct {
	ID              int64
	Title           string
	Description     string
	Link            string
	RSSFeedID       int64
	PublicationDate time.Time
	GUID            *string
}

// DBSetItemReadState sets the item's read state for the user.
func DBSetItemReadState(db *sql.DB, id int64, userID int,
	state ReadState) error {
	// Upsert.
	query := `
INSERT INTO rss_item_state
(user_id, item_id, state)
VALUES($1, $2, $3)
ON CONFLICT (user_id, item_id) DO UPDATE
SET state = $4
`
	_, err := db.Exec(query, userID, id, state.String(), state.String())
	if err != nil {
		return fmt.Errorf("unable to set read state on item: %d: %s", id, err)
	}

	return nil
}

// Turn read state into the enumerated type in the database (read_state).
func (s ReadState) String() string {
	if s == Unread {
		return "unread"
	}
	if s == Read {
		return "read"
	}
	return "read-later"
}

// FindItemByLink retrieves an item's information from the database by feed and
// link. Link is unique per feed.
func FindItemByLink(db *sql.DB, feedID int64, link string) (*DBItem, error) {
	query := `
SELECT
id, title, description, link, rss_feed_id, publication_date, guid
FROM rss_item
WHERE rss_feed_id = $1 AND
link = $2
`

	row := db.QueryRow(query, feedID, link)
	item := &DBItem{}
	if err := row.Scan(
		&item.ID,
		&item.Title,
		&item.Description,
		&item.Link,
		&item.RSSFeedID,
		&item.PublicationDate,
		&item.GUID,
	); err != nil {
		return nil, fmt.Errorf("failed to scan row: %s", err)
	}

	return item, nil
}
<|endoftext|>


<commit_before>package Gotem
import (
	"syscall"
	"unsafe"
)

type Point struct {
	x, y int
}

var (
	// lazyDll acts as a sort of singleton.
	lazyDll *syscall.LazyDLL
)

func MoveMouseTo(p Point) {
	if lazyDll == nil {
		lazyDll = syscall.NewLazyDLL("user32.dll")
	}
	proc := lazyDll.NewProc("SetPhysicalCursorPos")
	proc.Call(uintptr(p.x),uintptr(p.y))
}

func GetMousePosition() Point{
	type mosPoint struct {
		x, y int32
	}
	if lazyDll == nil {
		lazyDll = syscall.NewLazyDLL("user32.dll")
	}
	var ran mosPoint
	proc := lazyDll.NewProc("GetPhysicalCursorPos")
	proc.Call(uintptr(unsafe.Pointer(&ran)))
	return Point{int(ran.x),int(ran.y)}

}

func PressKey(key uintptr, isUp bool) {
	if lazyDll == nil {
		lazyDll = syscall.NewLazyDLL("user32.dll")
	}
	// The reason I am using keybd_event instead of SendInput is because I have no idea how to create
	// the Input structure that is needed to be passed in using Go since it uses a union.
	proc := lazyDll.NewProc("keybd_event")
	var dwFlags uintptr
	if isUp {
		dwFlags = 0x02
	} else {
		dwFlags = 0x0
	}
	proc.Call(key, 0, dwFlags, 0)
}

<commit_msg>Add main function<commit_after>package Gotem
import (
	"syscall"
	"unsafe"
)

type Point struct {
	x, y int
}

var (
	// lazyDll acts as a sort of singleton.
	lazyDll *syscall.LazyDLL
)

func MoveMouseTo(p Point) {
	if lazyDll == nil {
		lazyDll = syscall.NewLazyDLL("user32.dll")
	}
	proc := lazyDll.NewProc("SetPhysicalCursorPos")
	proc.Call(uintptr(p.x),uintptr(p.y))
}

func GetMousePosition() Point{
	type mosPoint struct {
		x, y int32
	}
	if lazyDll == nil {
		lazyDll = syscall.NewLazyDLL("user32.dll")
	}
	var ran mosPoint
	proc := lazyDll.NewProc("GetPhysicalCursorPos")
	proc.Call(uintptr(unsafe.Pointer(&ran)))
	return Point{int(ran.x),int(ran.y)}

}

func PressKey(key uintptr, isUp bool) {
	if lazyDll == nil {
		lazyDll = syscall.NewLazyDLL("user32.dll")
	}
	// The reason I am using keybd_event instead of SendInput is because I have no idea how to create
	// the Input structure that is needed to be passed in using Go since it uses a union.
	proc := lazyDll.NewProc("keybd_event")
	var dwFlags uintptr
	if isUp {
		dwFlags = 0x02
	} else {
		dwFlags = 0x0
	}
	proc.Call(key, 0, dwFlags, 0)
}

// for some reason I need a main even if it's a library?
func main() {}
<|endoftext|>


<commit_before>// Package react returns a random reaction parsed from a json file, see example json file for structure
package react

import (
	"flag"
	"fmt"
	"log"
	"regexp"

	"github.com/nboughton/config/parser"
	"github.com/nboughton/utils"
	"golang.org/x/exp/inotify"
)

// Item is the match text and possible responses for a reacion
type Item struct {
	Text string
	Resp []string
}

// React contains reactions from a reactions.json file
type React struct {
	Items []Item
}

var (
	// Reactions is the struct that the reactions.json file is parsed into
	Reactions  React
	reactRegex *regexp.Regexp
	reactFile  *string
)

func init() {
	reactFile = flag.String("react", "reactions.json", "Path to reactions file")
	flag.Parse()

	readFile()
	genRegex()

	watcher, err := inotify.NewWatcher()
	if err != nil {
		log.Panicln(err)
	}

	err = watcher.Watch(*reactFile)
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		for {
			select {
			case ev := <-watcher.Event:
				// if modified reload
				if ev.Mask == inotify.IN_MODIFY {
					readFile()
					genRegex()
				}

			case err := <-watcher.Error:
				log.Println("error:", err)

			}
		}
	}()
}

// Match tests to see if a string matches a listed string
func Match(s string) bool {
	return reactRegex.MatchString(s)
}

// Respond returns a random response from the first reaction to match the generated regex
func Respond(s string) (string, error) {
	str := reactRegex.FindAllString(s, 1)[0]
	for _, r := range Reactions.Items {
		if r.Text == str {
			return utils.RandS(r.Resp), nil
		}
	}
	return "", fmt.Errorf("No response found")
}

func readFile() {
	err := parser.NewParser(*reactFile).Scan(&Reactions)
	if err != nil {
		log.Panicln(err)
	}
}

func genRegex() {
	s := "("
	for idx, i := range Reactions.Items {
		s += regexp.QuoteMeta(i.Text)
		if idx < len(Reactions.Items)-1 {
			s += "|"
		}
	}
	s += ")"

	reactRegex = regexp.MustCompile(s)
}
<commit_msg>fixed modify reload<commit_after>// Package react returns a random reaction parsed from a json file, see example json file for structure
package react

import (
	"flag"
	"fmt"
	"log"
	"regexp"

	"github.com/nboughton/config/parser"
	"github.com/nboughton/utils"
	"golang.org/x/exp/inotify"
)

// Item is the match text and possible responses for a reacion
type Item struct {
	Text string
	Resp []string
}

// React contains reactions from a reactions.json file
type React struct {
	Items []Item
}

var (
	// Reactions is the struct that the reactions.json file is parsed into
	Reactions  React
	reactRegex *regexp.Regexp
	reactFile  *string
)

func init() {
	reactFile = flag.String("react", "reactions.json", "Path to reactions file")
	flag.Parse()

	readFile()
	genRegex()

	watcher, err := inotify.NewWatcher()
	if err != nil {
		log.Panicln(err)
	}

	err = watcher.Watch(*reactFile)
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		for {
			select {
			case ev := <-watcher.Event:
				// if modified reload
				if ev.Mask == inotify.IN_CLOSE_WRITE {
					readFile()
					genRegex()
				}

			case err := <-watcher.Error:
				log.Println("error:", err)

			}
		}
	}()
}

// Match tests to see if a string matches a listed string
func Match(s string) bool {
	return reactRegex.MatchString(s)
}

// Respond returns a random response from the first reaction to match the generated regex
func Respond(s string) (string, error) {
	str := reactRegex.FindAllString(s, 1)[0]
	for _, r := range Reactions.Items {
		if r.Text == str {
			return utils.RandS(r.Resp), nil
		}
	}
	return "", fmt.Errorf("No response found")
}

func readFile() {
	err := parser.NewParser(*reactFile).Scan(&Reactions)
	if err != nil {
		log.Panicln(err)
	}
}

func genRegex() {
	s := "("
	for idx, i := range Reactions.Items {
		s += regexp.QuoteMeta(i.Text)
		if idx < len(Reactions.Items)-1 {
			s += "|"
		}
	}
	s += ")"

	reactRegex = regexp.MustCompile(s)
}
<|endoftext|>


<commit_before>package fit_test

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/tormoder/fit"
)

var (
	update  = flag.Bool("update", false, "update .golden output and table for decode test files if their fingerprint differs")
	fupdate = flag.Bool("fupdate", false, "force regeneration of decode test files table")
	fdecode = flag.Bool("fdecode", false, "force decode golden part of decode test irregardless of Go version")
)

var (
	activitySmallMu   sync.Mutex
	activitySmallOnce sync.Once
	activitySmallData []byte
)

func activitySmall() []byte {
	activitySmallMu.Lock()
	defer activitySmallMu.Unlock()
	activitySmallOnce.Do(func() {
		asd, err := ioutil.ReadFile(activitySmallPath)
		if err != nil {
			errDesc := fmt.Sprintf("parseActivitySmallData failed: %v", err)
			panic(errDesc)
		}
		activitySmallData = asd
	})
	return activitySmallData
}

var (
	activitySmallPath      = filepath.Join(tdfolder, "me", "activity-small-fenix2-run.fit")
	activityLargePath      = filepath.Join(tdfolder, "me", "activity-large-fenxi2-multisport.fit")
	activityComponentsPath = filepath.Join(tdfolder, "dcrainmaker", "Edge810-Vector-2013-08-16-15-35-10.fit")
	monitoringPath         = filepath.Join(tdfolder, "fitsdk", "MonitoringFile.fit")
)

const (
	goldenSuffix  = ".golden"
	currentSuffix = ".current"
	gzSuffix      = ".gz"
	tdfolder      = "testdata"
)

func TestMain(m *testing.M) {
	flag.Parse()
	os.Exit(m.Run())
}

func TestDecode(t *testing.T) {
	const goMajorVersionForDecodeGolden = "go1.8"
	testDecodeGolden := true
	goVersion := runtime.Version()
	goVersionOK := strings.HasPrefix(goVersion, goMajorVersionForDecodeGolden)
	switch {
	case !goVersionOK && !*fdecode:
		testDecodeGolden = false
		t.Logf(
			"skipping golden decode part of test due to Go version (enabled for %s.x, have %q)",
			goMajorVersionForDecodeGolden,
			goVersion,
		)
	case !goVersionOK && *fdecode:
		t.Logf(
			"override: performing golden decode part of test for Go version %q (default only for %s.x)",
			goVersion,
			goMajorVersionForDecodeGolden,
		)
	default:
	}

	regenTestTable := struct {
		sync.Mutex // Protects val and decodeTestFiles slice in reader_util_test.go.
		val        bool
	}{}

	t.Run("Group", func(t *testing.T) {
		for i, file := range decodeTestFiles {
			i, file := i, file // Capture range variables.
			t.Run(fmt.Sprintf("%s/%s", file.folder, file.name), func(t *testing.T) {
				t.Parallel()
				fpath := filepath.Join(tdfolder, file.folder, file.name)
				data, err := ioutil.ReadFile(fpath)
				if err != nil {
					t.Fatalf("reading file failed: %v", err)
				}
				fitFile, err := fit.Decode(bytes.NewReader(data), file.dopts.opts()...)
				if !file.wantErr && err != nil {
					t.Fatalf("got error, want none; error is: %v", err)
				}
				if file.wantErr && err == nil {
					t.Fatalf("got no error, want error")
				}
				if !testDecodeGolden {
					return
				}
				if file.fingerprint == 0 {
					return
				}
				fp := fitFingerprint(fitFile)
				if fp == file.fingerprint {
					return
				}
				t.Errorf("fit file fingerprint differs: got: %d, want: %d", fp, file.fingerprint)
				if !*update {
					fpath = fpath + currentSuffix
				} else {
					fpath = fpath + goldenSuffix
				}
				if file.compress {
					fpath = fpath + gzSuffix
				}
				err = fitUtterDump(fitFile, fpath, file.compress)
				if err != nil {
					t.Fatalf("error writing output: %v", err)
				}
				if !*update {
					t.Logf("current output written to: %s", fpath)
					t.Logf("use a diff tool to compare (e.g. zdiff if compressed)")
				} else {
					regenTestTable.Lock()
					regenTestTable.val = true
					decodeTestFiles[i].fingerprint = fp
					regenTestTable.Unlock()
					t.Logf("%q has been updated", fpath)
					t.Logf("new fingerprint is: %d, update test case in reader_test.go", fp)
				}
			})
		}
	})

	if regenTestTable.val || *fupdate {
		t.Logf("regenerating table for decode test files...")
		err := regenerateDecodeTestTable()
		if err != nil {
			t.Fatalf("error regenerating table for decode test files: %v", err)
		}
	}
}

func TestDecodeChained(t *testing.T) {
	chainedTestFiles := []struct {
		fpath   string
		dfiles  int
		wantErr bool
		desc    string
	}{
		{
			filepath.Join(tdfolder, "fitsdk", "Activity.fit"),
			1,
			false,
			"single valid fit file",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-settings.fit"),
			2,
			false,
			"two valid chained fit files",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-activity-filecrc.fit"),
			2,
			true,
			"one valid fit file + one fit file with wrong crc",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-settings-corruptheader.fit"),
			1,
			true,
			"one valid fit file + one fit file with corrupt header",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-settings-nodata.fit"),
			2,
			true,
			"one valid fit file + one fit file with ok header but no data",
		},
	}

	for _, ctf := range chainedTestFiles {
		ctf := ctf
		t.Run(ctf.fpath, func(t *testing.T) {
			t.Parallel()
			data, err := ioutil.ReadFile(ctf.fpath)
			if err != nil {
				t.Fatalf("reading file data failed: %v", err)
			}
			fitFiles, err := fit.DecodeChained(bytes.NewReader(data))
			if !ctf.wantErr && err != nil {
				t.Fatalf("got error, want none; error is: %v", err)
			}
			if ctf.wantErr && err == nil {
				t.Fatalf("got no error, want error")
			}
			if len(fitFiles) != ctf.dfiles {
				t.Fatalf("got %d decoded fit file(s), want %d", len(fitFiles), ctf.dfiles)
			}
		})
	}
}

func TestCheckIntegrity(t *testing.T) {
	t.Run("ActivitySmall", func(t *testing.T) {
		err := fit.CheckIntegrity(bytes.NewReader(activitySmall()), false)
		if err != nil {
			t.Errorf("%q: failed: %v", activitySmallPath, err)
		}
	})
	t.Run("ActivitySDK", func(t *testing.T) {
		fpath := filepath.Join(tdfolder, "fitsdk", "Activity.fit")
		data, err := ioutil.ReadFile(fpath)
		if err != nil {
			t.Fatalf("reading %q failed: %v", fpath, err)
		}
		err = fit.CheckIntegrity(bytes.NewReader(data), false)
		if err != nil {
			t.Errorf("%q: failed: %v", fpath, err)
		}
	})
}

func TestDecodeHeader(t *testing.T) {
	wantHeader := fit.Header{
		Size:            0xe,
		ProtocolVersion: 0x10,
		ProfileVersion:  0x457,
		DataSize:        0x1dbdf,
		DataType:        [4]uint8{0x2e, 0x46, 0x49, 0x54},
		CRC:             0x1ec4,
	}
	gotHeader, err := fit.DecodeHeader(bytes.NewReader(activitySmall()))
	if err != nil {
		t.Errorf("%q: failed: %v", activitySmallPath, err)
	}
	if gotHeader != wantHeader {
		t.Errorf("got header:\n%#v\nwant header: %#v", gotHeader, wantHeader)
	}
}

func TestDecodeHeaderAndFileID(t *testing.T) {
	wantHeader := fit.Header{
		Size:            0xe,
		ProtocolVersion: 0x10,
		ProfileVersion:  0x457,
		DataSize:        0x1dbdf,
		DataType:        [4]uint8{0x2e, 0x46, 0x49, 0x54},
		CRC:             0x1ec4,
	}
	tc := time.Unix(1439652761, 0)
	tc = tc.UTC()
	wantFileId := fit.FileIdMsg{
		Type:         0x4,
		Manufacturer: 0x1,
		Product:      0x7af,
		SerialNumber: 0xe762d9cf,
		Number:       0xffff,
		TimeCreated:  tc,
		ProductName:  "",
	}

	gotHeader, gotFileId, err := fit.DecodeHeaderAndFileID(bytes.NewReader(activitySmall()))
	if err != nil {
		t.Errorf("%q: failed: %v", activitySmallPath, err)
	}
	if gotHeader != wantHeader {
		t.Errorf("%q:\ngot header:\n%#v\nwant header:\n%#v", activitySmallPath, gotHeader, wantHeader)
	}
	if gotFileId != wantFileId {
		t.Errorf("%q:\ngot FileIdMsg:\n%v\nwant FileIdMsg:\n%v", activitySmallPath, gotFileId, wantFileId)
	}
}

func BenchmarkDecode(b *testing.B) {
	files := []struct {
		desc, path string
	}{
		{"ActivitySmall", activitySmallPath},
		{"ActivityLarge", activityLargePath},
		{"ActivityWithComponents", activityComponentsPath},
		{"MonitoringFile", monitoringPath},
	}
	for _, file := range files {
		b.Run(file.desc, func(b *testing.B) {
			data, err := ioutil.ReadFile(file.path)
			if err != nil {
				b.Fatalf("%q: error reading file: %v", file.path, err)
			}
			b.ReportAllocs()
			b.SetBytes(int64(len(data)))
			b.ResetTimer()
			for i := 0; i < b.N; i++ {
				_, err := fit.Decode(bytes.NewReader(data))
				if err != nil {
					b.Fatalf("%q: error decoding file: %v", file.path, err)
				}
			}
		})
	}
}

func BenchmarkDecodeActivityLargeParallel(b *testing.B) {
	data, err := ioutil.ReadFile(activityLargePath)
	if err != nil {
		b.Fatal(err)
	}
	b.ReportAllocs()
	b.SetBytes(int64(len(data)))
	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			_, err := fit.Decode(bytes.NewReader(data))
			if err != nil {
				b.Fatal(err)
			}
		}
	})
}

func BenchmarkDecodeHeader(b *testing.B) {
	data := activitySmall()
	b.ReportAllocs()
	b.SetBytes(int64(len(data)))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := fit.DecodeHeader(bytes.NewReader(data))
		if err != nil {
			b.Fatalf("%q: error decoding header: %v", activitySmallPath, err)
		}
	}

}

func BenchmarkDecodeHeaderAndFileID(b *testing.B) {
	data := activitySmall()
	b.ReportAllocs()
	b.SetBytes(int64(len(data)))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _, err := fit.DecodeHeaderAndFileID(bytes.NewReader(data))
		if err != nil {
			b.Fatalf("%q: error decoding header/fileid: %v", activitySmallPath, err)
		}
	}
}
<commit_msg>reader_test: update Go version (1.9) used for golden decode tests<commit_after>package fit_test

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/tormoder/fit"
)

var (
	update  = flag.Bool("update", false, "update .golden output and table for decode test files if their fingerprint differs")
	fupdate = flag.Bool("fupdate", false, "force regeneration of decode test files table")
	fdecode = flag.Bool("fdecode", false, "force decode golden part of decode test irregardless of Go version")
)

var (
	activitySmallMu   sync.Mutex
	activitySmallOnce sync.Once
	activitySmallData []byte
)

func activitySmall() []byte {
	activitySmallMu.Lock()
	defer activitySmallMu.Unlock()
	activitySmallOnce.Do(func() {
		asd, err := ioutil.ReadFile(activitySmallPath)
		if err != nil {
			errDesc := fmt.Sprintf("parseActivitySmallData failed: %v", err)
			panic(errDesc)
		}
		activitySmallData = asd
	})
	return activitySmallData
}

var (
	activitySmallPath      = filepath.Join(tdfolder, "me", "activity-small-fenix2-run.fit")
	activityLargePath      = filepath.Join(tdfolder, "me", "activity-large-fenxi2-multisport.fit")
	activityComponentsPath = filepath.Join(tdfolder, "dcrainmaker", "Edge810-Vector-2013-08-16-15-35-10.fit")
	monitoringPath         = filepath.Join(tdfolder, "fitsdk", "MonitoringFile.fit")
)

const (
	goldenSuffix  = ".golden"
	currentSuffix = ".current"
	gzSuffix      = ".gz"
	tdfolder      = "testdata"
)

func TestMain(m *testing.M) {
	flag.Parse()
	os.Exit(m.Run())
}

func TestDecode(t *testing.T) {
	const goMajorVersionForDecodeGolden = "go1.9"
	testDecodeGolden := true
	goVersion := runtime.Version()
	goVersionOK := strings.HasPrefix(goVersion, goMajorVersionForDecodeGolden)
	switch {
	case !goVersionOK && !*fdecode:
		testDecodeGolden = false
		t.Logf(
			"skipping golden decode part of test due to Go version (enabled for %s.x, have %q)",
			goMajorVersionForDecodeGolden,
			goVersion,
		)
	case !goVersionOK && *fdecode:
		t.Logf(
			"override: performing golden decode part of test for Go version %q (default only for %s.x)",
			goVersion,
			goMajorVersionForDecodeGolden,
		)
	default:
	}

	regenTestTable := struct {
		sync.Mutex // Protects val and decodeTestFiles slice in reader_util_test.go.
		val        bool
	}{}

	t.Run("Group", func(t *testing.T) {
		for i, file := range decodeTestFiles {
			i, file := i, file // Capture range variables.
			t.Run(fmt.Sprintf("%s/%s", file.folder, file.name), func(t *testing.T) {
				t.Parallel()
				fpath := filepath.Join(tdfolder, file.folder, file.name)
				data, err := ioutil.ReadFile(fpath)
				if err != nil {
					t.Fatalf("reading file failed: %v", err)
				}
				fitFile, err := fit.Decode(bytes.NewReader(data), file.dopts.opts()...)
				if !file.wantErr && err != nil {
					t.Fatalf("got error, want none; error is: %v", err)
				}
				if file.wantErr && err == nil {
					t.Fatalf("got no error, want error")
				}
				if !testDecodeGolden {
					return
				}
				if file.fingerprint == 0 {
					return
				}
				fp := fitFingerprint(fitFile)
				if fp == file.fingerprint {
					return
				}
				t.Errorf("fit file fingerprint differs: got: %d, want: %d", fp, file.fingerprint)
				if !*update {
					fpath = fpath + currentSuffix
				} else {
					fpath = fpath + goldenSuffix
				}
				if file.compress {
					fpath = fpath + gzSuffix
				}
				err = fitUtterDump(fitFile, fpath, file.compress)
				if err != nil {
					t.Fatalf("error writing output: %v", err)
				}
				if !*update {
					t.Logf("current output written to: %s", fpath)
					t.Logf("use a diff tool to compare (e.g. zdiff if compressed)")
				} else {
					regenTestTable.Lock()
					regenTestTable.val = true
					decodeTestFiles[i].fingerprint = fp
					regenTestTable.Unlock()
					t.Logf("%q has been updated", fpath)
					t.Logf("new fingerprint is: %d, update test case in reader_test.go", fp)
				}
			})
		}
	})

	if regenTestTable.val || *fupdate {
		t.Logf("regenerating table for decode test files...")
		err := regenerateDecodeTestTable()
		if err != nil {
			t.Fatalf("error regenerating table for decode test files: %v", err)
		}
	}
}

func TestDecodeChained(t *testing.T) {
	chainedTestFiles := []struct {
		fpath   string
		dfiles  int
		wantErr bool
		desc    string
	}{
		{
			filepath.Join(tdfolder, "fitsdk", "Activity.fit"),
			1,
			false,
			"single valid fit file",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-settings.fit"),
			2,
			false,
			"two valid chained fit files",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-activity-filecrc.fit"),
			2,
			true,
			"one valid fit file + one fit file with wrong crc",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-settings-corruptheader.fit"),
			1,
			true,
			"one valid fit file + one fit file with corrupt header",
		},
		{
			filepath.Join(tdfolder, "chained", "activity-settings-nodata.fit"),
			2,
			true,
			"one valid fit file + one fit file with ok header but no data",
		},
	}

	for _, ctf := range chainedTestFiles {
		ctf := ctf
		t.Run(ctf.fpath, func(t *testing.T) {
			t.Parallel()
			data, err := ioutil.ReadFile(ctf.fpath)
			if err != nil {
				t.Fatalf("reading file data failed: %v", err)
			}
			fitFiles, err := fit.DecodeChained(bytes.NewReader(data))
			if !ctf.wantErr && err != nil {
				t.Fatalf("got error, want none; error is: %v", err)
			}
			if ctf.wantErr && err == nil {
				t.Fatalf("got no error, want error")
			}
			if len(fitFiles) != ctf.dfiles {
				t.Fatalf("got %d decoded fit file(s), want %d", len(fitFiles), ctf.dfiles)
			}
		})
	}
}

func TestCheckIntegrity(t *testing.T) {
	t.Run("ActivitySmall", func(t *testing.T) {
		err := fit.CheckIntegrity(bytes.NewReader(activitySmall()), false)
		if err != nil {
			t.Errorf("%q: failed: %v", activitySmallPath, err)
		}
	})
	t.Run("ActivitySDK", func(t *testing.T) {
		fpath := filepath.Join(tdfolder, "fitsdk", "Activity.fit")
		data, err := ioutil.ReadFile(fpath)
		if err != nil {
			t.Fatalf("reading %q failed: %v", fpath, err)
		}
		err = fit.CheckIntegrity(bytes.NewReader(data), false)
		if err != nil {
			t.Errorf("%q: failed: %v", fpath, err)
		}
	})
}

func TestDecodeHeader(t *testing.T) {
	wantHeader := fit.Header{
		Size:            0xe,
		ProtocolVersion: 0x10,
		ProfileVersion:  0x457,
		DataSize:        0x1dbdf,
		DataType:        [4]uint8{0x2e, 0x46, 0x49, 0x54},
		CRC:             0x1ec4,
	}
	gotHeader, err := fit.DecodeHeader(bytes.NewReader(activitySmall()))
	if err != nil {
		t.Errorf("%q: failed: %v", activitySmallPath, err)
	}
	if gotHeader != wantHeader {
		t.Errorf("got header:\n%#v\nwant header: %#v", gotHeader, wantHeader)
	}
}

func TestDecodeHeaderAndFileID(t *testing.T) {
	wantHeader := fit.Header{
		Size:            0xe,
		ProtocolVersion: 0x10,
		ProfileVersion:  0x457,
		DataSize:        0x1dbdf,
		DataType:        [4]uint8{0x2e, 0x46, 0x49, 0x54},
		CRC:             0x1ec4,
	}
	tc := time.Unix(1439652761, 0)
	tc = tc.UTC()
	wantFileId := fit.FileIdMsg{
		Type:         0x4,
		Manufacturer: 0x1,
		Product:      0x7af,
		SerialNumber: 0xe762d9cf,
		Number:       0xffff,
		TimeCreated:  tc,
		ProductName:  "",
	}

	gotHeader, gotFileId, err := fit.DecodeHeaderAndFileID(bytes.NewReader(activitySmall()))
	if err != nil {
		t.Errorf("%q: failed: %v", activitySmallPath, err)
	}
	if gotHeader != wantHeader {
		t.Errorf("%q:\ngot header:\n%#v\nwant header:\n%#v", activitySmallPath, gotHeader, wantHeader)
	}
	if gotFileId != wantFileId {
		t.Errorf("%q:\ngot FileIdMsg:\n%v\nwant FileIdMsg:\n%v", activitySmallPath, gotFileId, wantFileId)
	}
}

func BenchmarkDecode(b *testing.B) {
	files := []struct {
		desc, path string
	}{
		{"ActivitySmall", activitySmallPath},
		{"ActivityLarge", activityLargePath},
		{"ActivityWithComponents", activityComponentsPath},
		{"MonitoringFile", monitoringPath},
	}
	for _, file := range files {
		b.Run(file.desc, func(b *testing.B) {
			data, err := ioutil.ReadFile(file.path)
			if err != nil {
				b.Fatalf("%q: error reading file: %v", file.path, err)
			}
			b.ReportAllocs()
			b.SetBytes(int64(len(data)))
			b.ResetTimer()
			for i := 0; i < b.N; i++ {
				_, err := fit.Decode(bytes.NewReader(data))
				if err != nil {
					b.Fatalf("%q: error decoding file: %v", file.path, err)
				}
			}
		})
	}
}

func BenchmarkDecodeActivityLargeParallel(b *testing.B) {
	data, err := ioutil.ReadFile(activityLargePath)
	if err != nil {
		b.Fatal(err)
	}
	b.ReportAllocs()
	b.SetBytes(int64(len(data)))
	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			_, err := fit.Decode(bytes.NewReader(data))
			if err != nil {
				b.Fatal(err)
			}
		}
	})
}

func BenchmarkDecodeHeader(b *testing.B) {
	data := activitySmall()
	b.ReportAllocs()
	b.SetBytes(int64(len(data)))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := fit.DecodeHeader(bytes.NewReader(data))
		if err != nil {
			b.Fatalf("%q: error decoding header: %v", activitySmallPath, err)
		}
	}

}

func BenchmarkDecodeHeaderAndFileID(b *testing.B) {
	data := activitySmall()
	b.ReportAllocs()
	b.SetBytes(int64(len(data)))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _, err := fit.DecodeHeaderAndFileID(bytes.NewReader(data))
		if err != nil {
			b.Fatalf("%q: error decoding header/fileid: %v", activitySmallPath, err)
		}
	}
}
<|endoftext|>


<commit_before>package swarm

import "time"

// Service represents a service.
type Service struct {
	ID string
	Meta
	Spec         ServiceSpec  `json:",omitempty"`
	PreviousSpec *ServiceSpec `json:",omitempty"`
	Endpoint     Endpoint     `json:",omitempty"`
	UpdateStatus UpdateStatus `json:",omitempty"`
}

// ServiceSpec represents the spec of a service.
type ServiceSpec struct {
	Annotations

	// TaskTemplate defines how the service should construct new tasks when
	// orchestrating this service.
	TaskTemplate TaskSpec      `json:",omitempty"`
	Mode         ServiceMode   `json:",omitempty"`
	UpdateConfig *UpdateConfig `json:",omitempty"`

	// Networks field in ServiceSpec is deprecated. The
	// same field in TaskSpec should be used instead.
	// This field will be removed in a future release.
	Networks     []NetworkAttachmentConfig `json:",omitempty"`
	EndpointSpec *EndpointSpec             `json:",omitempty"`
}

// ServiceMode represents the mode of a service.
type ServiceMode struct {
	Replicated *ReplicatedService `json:",omitempty"`
	Global     *GlobalService     `json:",omitempty"`
}

// UpdateState is the state of a service update.
type UpdateState string

const (
	// UpdateStateUpdating is the updating state.
	UpdateStateUpdating UpdateState = "updating"
	// UpdateStatePaused is the paused state.
	UpdateStatePaused UpdateState = "paused"
	// UpdateStateCompleted is the completed state.
	UpdateStateCompleted UpdateState = "completed"
)

// UpdateStatus reports the status of a service update.
type UpdateStatus struct {
	State       UpdateState `json:",omitempty"`
	StartedAt   time.Time   `json:",omitempty"`
	CompletedAt time.Time   `json:",omitempty"`
	Message     string      `json:",omitempty"`
}

// ReplicatedService is a kind of ServiceMode.
type ReplicatedService struct {
	Replicas *uint64 `json:",omitempty"`
}

// GlobalService is a kind of ServiceMode.
type GlobalService struct{}

const (
	// UpdateFailureActionPause PAUSE
	UpdateFailureActionPause = "pause"
	// UpdateFailureActionContinue CONTINUE
	UpdateFailureActionContinue = "continue"
)

// UpdateConfig represents the update configuration.
type UpdateConfig struct {
	// Maximum number of tasks to be updated in one iteration.
	// 0 means unlimited parallelism.
	Parallelism uint64 `json:",omitempty"`

	// Amount of time between updates.
	Delay time.Duration `json:",omitempty"`

	// FailureAction is the action to take when an update failures.
	FailureAction string `json:",omitempty"`

	// Monitor indicates how long to monitor a task for failure after it is
	// created. If the task fails by ending up in one of the states
	// REJECTED, COMPLETED, or FAILED, within Monitor from its creation,
	// this counts as a failure. If it fails after Monitor, it does not
	// count as a failure. If Monitor is unspecified, a default value will
	// be used.
	Monitor time.Duration `json:",omitempty"`

	// MaxFailureRatio is the fraction of tasks that may fail during
	// an update before the failure action is invoked. Any task created by
	// the current update which ends up in one of the states REJECTED,
	// COMPLETED or FAILED within Monitor from its creation counts as a
	// failure. The number of failures is divided by the number of tasks
	// being updated, and if this fraction is greater than
	// MaxFailureRatio, the failure action is invoked.
	//
	// If the failure action is CONTINUE, there is no effect.
	// If the failure action is PAUSE, no more tasks will be updated until
	// another update is started.
	MaxFailureRatio float32
}
<commit_msg>api: Remove omitempty tag on Parallelism<commit_after>package swarm

import "time"

// Service represents a service.
type Service struct {
	ID string
	Meta
	Spec         ServiceSpec  `json:",omitempty"`
	PreviousSpec *ServiceSpec `json:",omitempty"`
	Endpoint     Endpoint     `json:",omitempty"`
	UpdateStatus UpdateStatus `json:",omitempty"`
}

// ServiceSpec represents the spec of a service.
type ServiceSpec struct {
	Annotations

	// TaskTemplate defines how the service should construct new tasks when
	// orchestrating this service.
	TaskTemplate TaskSpec      `json:",omitempty"`
	Mode         ServiceMode   `json:",omitempty"`
	UpdateConfig *UpdateConfig `json:",omitempty"`

	// Networks field in ServiceSpec is deprecated. The
	// same field in TaskSpec should be used instead.
	// This field will be removed in a future release.
	Networks     []NetworkAttachmentConfig `json:",omitempty"`
	EndpointSpec *EndpointSpec             `json:",omitempty"`
}

// ServiceMode represents the mode of a service.
type ServiceMode struct {
	Replicated *ReplicatedService `json:",omitempty"`
	Global     *GlobalService     `json:",omitempty"`
}

// UpdateState is the state of a service update.
type UpdateState string

const (
	// UpdateStateUpdating is the updating state.
	UpdateStateUpdating UpdateState = "updating"
	// UpdateStatePaused is the paused state.
	UpdateStatePaused UpdateState = "paused"
	// UpdateStateCompleted is the completed state.
	UpdateStateCompleted UpdateState = "completed"
)

// UpdateStatus reports the status of a service update.
type UpdateStatus struct {
	State       UpdateState `json:",omitempty"`
	StartedAt   time.Time   `json:",omitempty"`
	CompletedAt time.Time   `json:",omitempty"`
	Message     string      `json:",omitempty"`
}

// ReplicatedService is a kind of ServiceMode.
type ReplicatedService struct {
	Replicas *uint64 `json:",omitempty"`
}

// GlobalService is a kind of ServiceMode.
type GlobalService struct{}

const (
	// UpdateFailureActionPause PAUSE
	UpdateFailureActionPause = "pause"
	// UpdateFailureActionContinue CONTINUE
	UpdateFailureActionContinue = "continue"
)

// UpdateConfig represents the update configuration.
type UpdateConfig struct {
	// Maximum number of tasks to be updated in one iteration.
	// 0 means unlimited parallelism.
	Parallelism uint64

	// Amount of time between updates.
	Delay time.Duration `json:",omitempty"`

	// FailureAction is the action to take when an update failures.
	FailureAction string `json:",omitempty"`

	// Monitor indicates how long to monitor a task for failure after it is
	// created. If the task fails by ending up in one of the states
	// REJECTED, COMPLETED, or FAILED, within Monitor from its creation,
	// this counts as a failure. If it fails after Monitor, it does not
	// count as a failure. If Monitor is unspecified, a default value will
	// be used.
	Monitor time.Duration `json:",omitempty"`

	// MaxFailureRatio is the fraction of tasks that may fail during
	// an update before the failure action is invoked. Any task created by
	// the current update which ends up in one of the states REJECTED,
	// COMPLETED or FAILED within Monitor from its creation counts as a
	// failure. The number of failures is divided by the number of tasks
	// being updated, and if this fraction is greater than
	// MaxFailureRatio, the failure action is invoked.
	//
	// If the failure action is CONTINUE, there is no effect.
	// If the failure action is PAUSE, no more tasks will be updated until
	// another update is started.
	MaxFailureRatio float32
}
<|endoftext|>