<commit_before>package convo

import (
	"bytes"
	"encoding/json"
	"time"

	"github.com/synapse-garden/sg-proto/store"

	"github.com/boltdb/bolt"
)

// Message is a container for a convo message.
type Message struct {
	Sender    string    `json:"sender"`
	Content   string    `json:"content"`
	Timestamp time.Time `json:"timestamp"`
}

// GetMessageRange gets a slice of up to max Messages for the given
// time range in the given Convo.
func GetMessageRange(
	convoID string,
	from, to time.Time,
	max int,
	tx *bolt.Tx,
) ([]Message, error) {
	b, err := store.GetNestedBucket(
		tx.Bucket(MessageBucket),
		store.Bucket(convoID),
	)
	switch {
	case store.IsMissingBucket(err):
		return nil, errMissing(err.(store.ErrMissingBucket))
	case err != nil:
		return nil, err
	}

	c := b.Cursor()
	fromBytes := from.AppendFormat(nil, time.RFC3339)
	toBytes := to.AppendFormat(nil, time.RFC3339)
	t, msg := c.Seek(toBytes)
	switch {
	case t != nil && bytes.Compare(t, toBytes) > 0:
		// Seek gave us something too new.  Try previous.
		if t, msg = c.Prev(); t == nil {
			// It was the first item.
			return []Message{}, nil
		} else if bytes.Compare(t, fromBytes) < 0 {
			// Something older was found, but too old.
			return []Message{}, nil
		}
	case t == nil:
		// Nothing that new exists at all.  Try previous.
		if t, msg = c.Prev(); t == nil {
			// There were no messages in the bucket at all.
			return []Message{}, nil
		} else if bytes.Compare(t, fromBytes) < 0 {
			// There were previous messages, but too old.
			return []Message{}, nil
		}
	}
	// We now have our cursor at the end of our range.

	var result []Message
	for t != nil && len(result) < max && bytes.Compare(t, fromBytes) >= 0 {
		next := Message{}
		if err := json.Unmarshal(msg, &next); err != nil {
			return nil, err
		}
		result = append(result, next)
		t, msg = c.Prev()
	}

	// Since we iterated backwards, reverse the array now.
	lenRes := len(result)
	mp := lenRes / 2
	for i := 0; i < mp; i++ {
		result[lenRes-i-1], result[i] = result[i], result[lenRes-i-1]
	}

	return result, nil
}

// GetMessages gets a slice of up to 10 Messages for the last week in
// the given Convo.
func GetMessages(convoID string, tx *bolt.Tx) ([]Message, error) {
	now := time.Now()
	then := now.Add(-7 * 24 * time.Hour)
	return GetMessageRange(convoID, now, then, 50, tx)
}
<commit_msg>Switch GetMessages to fix bug<commit_after>package convo

import (
	"bytes"
	"encoding/json"
	"time"

	"github.com/synapse-garden/sg-proto/store"

	"github.com/boltdb/bolt"
)

// Message is a container for a convo message.
type Message struct {
	Sender    string    `json:"sender"`
	Content   string    `json:"content"`
	Timestamp time.Time `json:"timestamp"`
}

// GetMessageRange gets a slice of up to max Messages for the given
// time range in the given Convo.
func GetMessageRange(
	convoID string,
	from, to time.Time,
	max int,
	tx *bolt.Tx,
) ([]Message, error) {
	b, err := store.GetNestedBucket(
		tx.Bucket(MessageBucket),
		store.Bucket(convoID),
	)
	switch {
	case store.IsMissingBucket(err):
		return nil, errMissing(err.(store.ErrMissingBucket))
	case err != nil:
		return nil, err
	}

	c := b.Cursor()
	fromBytes := from.AppendFormat(nil, time.RFC3339)
	toBytes := to.AppendFormat(nil, time.RFC3339)
	t, msg := c.Seek(toBytes)
	switch {
	case t != nil && bytes.Compare(t, toBytes) > 0:
		// Seek gave us something too new.  Try previous.
		if t, msg = c.Prev(); t == nil {
			// It was the first item.
			return []Message{}, nil
		} else if bytes.Compare(t, fromBytes) < 0 {
			// Something older was found, but too old.
			return []Message{}, nil
		}
	case t == nil:
		// Nothing that new exists at all.  Try previous.
		if t, msg = c.Prev(); t == nil {
			// There were no messages in the bucket at all.
			return []Message{}, nil
		} else if bytes.Compare(t, fromBytes) < 0 {
			// There were previous messages, but too old.
			return []Message{}, nil
		}
	}
	// We now have our cursor at the end of our range.

	var result []Message
	for t != nil && len(result) < max && bytes.Compare(t, fromBytes) >= 0 {
		next := Message{}
		if err := json.Unmarshal(msg, &next); err != nil {
			return nil, err
		}
		result = append(result, next)
		t, msg = c.Prev()
	}

	// Since we iterated backwards, reverse the array now.
	lenRes := len(result)
	mp := lenRes / 2
	for i := 0; i < mp; i++ {
		result[lenRes-i-1], result[i] = result[i], result[lenRes-i-1]
	}

	return result, nil
}

// GetMessages gets a slice of up to 50 Messages for the last week in
// the given Convo.
func GetMessages(convoID string, tx *bolt.Tx) ([]Message, error) {
	now := time.Now()
	then := now.Add(-7 * 24 * time.Hour)
	return GetMessageRange(convoID, then, now, 50, tx)
}
<|endoftext|>


<commit_before>package tarantool

import (
	"bufio"
	"errors"
	"io"
	"net"
	"net/url"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var (
	ErrEmptyDefaultSpace = errors.New("zero-length default space or unnecessary slash in dsn.path")
	ErrSyncFailed        = errors.New("SYNC failed")
)

type Options struct {
	ConnectTimeout time.Duration
	QueryTimeout   time.Duration
	DefaultSpace   string
	User           string
	Password       string
	UUID           string
	ReplicaSetUUID string
	Perf           PerfCount
}

type Greeting struct {
	Version []byte
	Auth    []byte
}

type Connection struct {
	requestID uint64
	requests  *requestMap
	writeChan chan *BinaryPacket // packed messages with header
	closeOnce sync.Once
	exit      chan bool
	closed    chan bool
	tcpConn   net.Conn

	ccr io.Reader
	ccw io.Writer

	// options
	queryTimeout   time.Duration
	greeting       *Greeting
	packData       *packData
	remoteAddr     string
	firstError     error
	firstErrorLock *sync.Mutex
	perf           PerfCount
}

// Connect to tarantool instance with options.
// Returned Connection could be used to execute queries.
func Connect(dsnString string, options *Options) (conn *Connection, err error) {
	dsn, opts, err := parseOptions(dsnString, options)
	if err != nil {
		return nil, err
	}

	conn, err = newConn(dsn.Scheme, dsn.Host, opts)
	if err != nil {
		return
	}

	// set schema pulling deadline
	deadline := time.Now().Add(opts.ConnectTimeout)
	conn.tcpConn.SetDeadline(deadline)

	err = conn.pullSchema()
	if err != nil {
		conn.tcpConn.Close()
		conn = nil
		return
	}

	// remove deadline
	conn.tcpConn.SetDeadline(time.Time{})

	go conn.worker()

	return
}

func newConn(scheme, addr string, opts Options) (conn *Connection, err error) {

	defer func() { // close opened connection if error
		if err != nil && conn != nil {
			if conn.tcpConn != nil {
				conn.tcpConn.Close()
			}
			conn = nil
		}
	}()

	conn = &Connection{
		remoteAddr:     addr,
		requests:       newRequestMap(),
		writeChan:      make(chan *BinaryPacket, 256),
		exit:           make(chan bool),
		closed:         make(chan bool),
		firstErrorLock: &sync.Mutex{},
		packData:       newPackData(opts.DefaultSpace),
		queryTimeout:   opts.QueryTimeout,
		perf:           opts.Perf,
	}

	conn.tcpConn, err = net.DialTimeout(scheme, conn.remoteAddr, opts.ConnectTimeout)
	if err != nil {
		return nil, err
	}

	if conn.perf.NetRead != nil {
		conn.ccr = NewCountedReader(conn.tcpConn, conn.perf.NetRead)
	} else {
		conn.ccr = conn.tcpConn
	}

	if conn.perf.NetWrite != nil {
		conn.ccw = NewCountedWriter(conn.tcpConn, conn.perf.NetWrite)
	} else {
		conn.ccw = conn.tcpConn
	}

	greeting := make([]byte, 128)

	connectDeadline := time.Now().Add(opts.ConnectTimeout)
	conn.tcpConn.SetDeadline(connectDeadline)
	// removing deadline deferred
	defer conn.tcpConn.SetDeadline(time.Time{})

	_, err = io.ReadFull(conn.ccr, greeting)
	if err != nil {
		return
	}

	conn.greeting = &Greeting{
		Version: greeting[:64],
		Auth:    greeting[64:108],
	}

	// try to authenticate if user have been provided
	if len(opts.User) > 0 {
		requestID := conn.nextID()

		pp := packetPool.GetWithID(requestID)

		err = pp.packMsg(&Auth{
			User:         opts.User,
			Password:     opts.Password,
			GreetingAuth: conn.greeting.Auth,
		}, conn.packData)
		if err != nil {
			pp.Release()
			return
		}

		_, err = pp.WriteTo(conn.ccw)
		pp.Release()
		if err != nil {
			return
		}

		pp = packetPool.Get()
		defer pp.Release()

		if err = pp.readPacket(conn.ccr); err != nil {
			return
		}

		authResponse := &pp.packet
		if authResponse.requestID != requestID {
			err = ErrSyncFailed
			return
		}

		if authResponse.Result != nil && authResponse.Result.Error != nil {
			err = authResponse.Result.Error
			return
		}
	}

	return
}

func parseOptions(dsnString string, options *Options) (*url.URL, Options, error) {
	if options == nil {
		options = &Options{}
	}
	opts := *options // copy to new object

	// remove schema, if present

	// === for backward compatibility (only tcp despite of user wishes :)
	dsnString = strings.TrimPrefix(dsnString, "unix:")
	// ===

	// tcp is the default scheme
	switch {
	case strings.HasPrefix(dsnString, "tcp://"):
	case strings.HasPrefix(dsnString, "//"):
		dsnString = "tcp:" + dsnString
	default:
		dsnString = "tcp://" + dsnString
	}
	dsn, err := url.Parse(dsnString)
	if err != nil {
		return dsn, opts, err
	}

	if opts.ConnectTimeout.Nanoseconds() == 0 {
		opts.ConnectTimeout = DefaultConnectTimeout
	}
	if opts.QueryTimeout.Nanoseconds() == 0 {
		opts.QueryTimeout = DefaultQueryTimeout
	}

	if len(opts.User) == 0 {
		if user := dsn.User; user != nil {
			opts.User = user.Username()
			opts.Password, _ = user.Password()
		}
	}

	if len(opts.DefaultSpace) == 0 && len(dsn.Path) > 0 {
		path := strings.TrimPrefix(dsn.Path, "/")
		// check it if it is necessary
		switch {
		case len(path) == 0:
			return nil, opts, ErrEmptyDefaultSpace
		//case strings.IndexAny(path, "/ ,") != -1:
		//	return nil, opts, ErrBadDSNPath
		default:
			opts.DefaultSpace = path
		}
	}

	return dsn, opts, nil
}

func (conn *Connection) pullSchema() (err error) {
	// select space and index schema
	request := func(q Query) (*Result, error) {
		var err error

		requestID := conn.nextID()

		pp := packetPool.GetWithID(requestID)
		if err = pp.packMsg(q, conn.packData); err != nil {
			pp.Release()
			return nil, err
		}

		_, err = pp.WriteTo(conn.ccw)
		pp.Release()
		if err != nil {
			return nil, err
		}

		pp = packetPool.Get()
		defer pp.Release()

		if err = pp.readPacket(conn.ccr); err != nil {
			return nil, err
		}

		response := &pp.packet
		if response.requestID != requestID {
			return nil, errors.New("Bad response requestID")
		}

		if response.Result == nil {
			return nil, errors.New("Nil response result")
		}

		if response.Result.Error != nil {
			return nil, response.Result.Error
		}

		return response.Result, nil
	}

	res, err := request(&Select{
		Space:    ViewSpace,
		Key:      0,
		Iterator: IterAll,
	})
	if err != nil {
		return
	}

	for _, space := range res.Data {
		spaceID, _ := conn.packData.spaceNo(space[0])
		conn.packData.spaceMap[space[2].(string)] = spaceID
	}

	res, err = request(&Select{
		Space:    ViewIndex,
		Key:      0,
		Iterator: IterAll,
	})
	if err != nil {
		return
	}

	for _, index := range res.Data {
		spaceID, _ := conn.packData.fieldNo(index[0])
		indexID, _ := conn.packData.fieldNo(index[1])
		indexName := index[2].(string)
		indexAttr := index[4].(map[string]interface{}) // e.g: {"unique": true}
		indexFields := index[5].([]interface{})        // e.g: [[0 num] [1 str]]

		indexSpaceMap, exists := conn.packData.indexMap[spaceID]
		if !exists {
			indexSpaceMap = make(map[string]uint64)
			conn.packData.indexMap[spaceID] = indexSpaceMap
		}
		indexSpaceMap[indexName] = indexID

		// build list of primary key field numbers for this space, if the PK is detected
		if indexAttr != nil && indexID == 0 {
			if unique, ok := indexAttr["unique"]; ok && unique.(bool) {
				pk := make([]int, len(indexFields))
				for i := range indexFields {
					descr := indexFields[i].([]interface{})
					f, _ := conn.packData.fieldNo(descr[0])
					pk[i] = int(f)
				}
				conn.packData.primaryKeyMap[spaceID] = pk
			}
		}
	}

	return
}

func (conn *Connection) nextID() uint64 {
	return atomic.AddUint64(&conn.requestID, 1)
}

func (conn *Connection) stop() {
	conn.closeOnce.Do(func() {
		// debug.PrintStack()
		close(conn.exit)
		conn.tcpConn.Close()
		runtime.GC()
	})
}

func (conn *Connection) GetPrimaryKeyFields(space interface{}) ([]int, bool) {
	var spaceID uint64
	var err error

	if conn.packData == nil {
		return nil, false
	}
	if spaceID, err = conn.packData.spaceNo(space); err != nil {
		return nil, false
	}

	f, ok := conn.packData.primaryKeyMap[spaceID]
	return f, ok
}

func (conn *Connection) Close() {
	conn.stop()
	<-conn.closed
}

func (conn *Connection) String() string {
	return conn.remoteAddr
}

func (conn *Connection) IsClosed() bool {
	select {
	case <-conn.exit:
		return true
	default:
		return false
	}
}

func (conn *Connection) getError() error {
	conn.firstErrorLock.Lock()
	defer conn.firstErrorLock.Unlock()
	return conn.firstError
}

func (conn *Connection) setError(err error) {
	if err != nil && err != io.EOF {
		conn.firstErrorLock.Lock()
		if conn.firstError == nil {
			conn.firstError = err
		}
		conn.firstErrorLock.Unlock()
	}
}

func (conn *Connection) worker() {
	var wg sync.WaitGroup

	wg.Add(2)

	go func() {
		err := conn.writer()
		conn.setError(err)
		conn.stop()
		wg.Done()
	}()

	go func() {
		err := conn.reader()
		conn.setError(err)
		conn.stop()
		wg.Done()
	}()

	wg.Wait()

	// release all pending packets
	writeChan := conn.writeChan
	conn.writeChan = nil

CLEANUP_LOOP:
	for {
		select {
		case pp := <-writeChan:
			pp.Release()
		default:
			break CLEANUP_LOOP
		}
	}

	// send error reply to all pending requests
	conn.requests.CleanUp(func(req *request) {
		select {
		case req.replyChan <- &AsyncResult{
			Error:     ConnectionClosedError(conn),
			ErrorCode: ErrNoConnection,
		}:
		default:
		}
	})

	close(conn.closed)
}

func (conn *Connection) writer() (err error) {
	writeChan := conn.writeChan
	stopChan := conn.exit
	w := bufio.NewWriterSize(conn.ccw, DefaultWriterBufSize)

	wp := func(w io.Writer, packet *BinaryPacket) error {
		if conn.perf.NetPacketsOut != nil {
			conn.perf.NetPacketsOut.Add(1)
		}
		_, err := packet.WriteTo(w)
		packet.Release()
		return err
	}

WRITER_LOOP:
	for {
		select {
		case packet, ok := <-writeChan:
			if !ok {
				break WRITER_LOOP
			}
			if err = wp(w, packet); err != nil {
				break WRITER_LOOP
			}
		case <-stopChan:
			break WRITER_LOOP
		default:
			if err = w.Flush(); err != nil {
				break WRITER_LOOP
			}

			// same without flush
			select {
			case packet, ok := <-writeChan:
				if !ok {
					break WRITER_LOOP
				}
				if err = wp(w, packet); err != nil {
					break WRITER_LOOP
				}
			case <-stopChan:
				break WRITER_LOOP
			}
		}
	}

	return
}

func (conn *Connection) reader() (err error) {
	var pp *BinaryPacket
	var requestID uint64

	r := bufio.NewReaderSize(conn.ccr, DefaultReaderBufSize)

READER_LOOP:
	for {
		pp := packetPool.Get()
		if requestID, err = pp.readRawPacket(r); err != nil {
			break READER_LOOP
		}

		if conn.perf.NetPacketsIn != nil {
			conn.perf.NetPacketsIn.Add(1)
		}

		req := conn.requests.Pop(requestID)
		if req == nil {
			pp.Release()
			pp = nil
			continue
		}

		select {
		case req.replyChan <- &AsyncResult{0, nil, pp, conn, req.opaque}:
			pp = nil
		default:
		}
	}

	if pp != nil {
		pp.Release()
	}
	return
}
<commit_msg>Return back the opaque pointer on conn closure<commit_after>package tarantool

import (
	"bufio"
	"errors"
	"io"
	"net"
	"net/url"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var (
	ErrEmptyDefaultSpace = errors.New("zero-length default space or unnecessary slash in dsn.path")
	ErrSyncFailed        = errors.New("SYNC failed")
)

type Options struct {
	ConnectTimeout time.Duration
	QueryTimeout   time.Duration
	DefaultSpace   string
	User           string
	Password       string
	UUID           string
	ReplicaSetUUID string
	Perf           PerfCount
}

type Greeting struct {
	Version []byte
	Auth    []byte
}

type Connection struct {
	requestID uint64
	requests  *requestMap
	writeChan chan *BinaryPacket // packed messages with header
	closeOnce sync.Once
	exit      chan bool
	closed    chan bool
	tcpConn   net.Conn

	ccr io.Reader
	ccw io.Writer

	// options
	queryTimeout   time.Duration
	greeting       *Greeting
	packData       *packData
	remoteAddr     string
	firstError     error
	firstErrorLock *sync.Mutex
	perf           PerfCount
}

// Connect to tarantool instance with options.
// Returned Connection could be used to execute queries.
func Connect(dsnString string, options *Options) (conn *Connection, err error) {
	dsn, opts, err := parseOptions(dsnString, options)
	if err != nil {
		return nil, err
	}

	conn, err = newConn(dsn.Scheme, dsn.Host, opts)
	if err != nil {
		return
	}

	// set schema pulling deadline
	deadline := time.Now().Add(opts.ConnectTimeout)
	conn.tcpConn.SetDeadline(deadline)

	err = conn.pullSchema()
	if err != nil {
		conn.tcpConn.Close()
		conn = nil
		return
	}

	// remove deadline
	conn.tcpConn.SetDeadline(time.Time{})

	go conn.worker()

	return
}

func newConn(scheme, addr string, opts Options) (conn *Connection, err error) {

	defer func() { // close opened connection if error
		if err != nil && conn != nil {
			if conn.tcpConn != nil {
				conn.tcpConn.Close()
			}
			conn = nil
		}
	}()

	conn = &Connection{
		remoteAddr:     addr,
		requests:       newRequestMap(),
		writeChan:      make(chan *BinaryPacket, 256),
		exit:           make(chan bool),
		closed:         make(chan bool),
		firstErrorLock: &sync.Mutex{},
		packData:       newPackData(opts.DefaultSpace),
		queryTimeout:   opts.QueryTimeout,
		perf:           opts.Perf,
	}

	conn.tcpConn, err = net.DialTimeout(scheme, conn.remoteAddr, opts.ConnectTimeout)
	if err != nil {
		return nil, err
	}

	if conn.perf.NetRead != nil {
		conn.ccr = NewCountedReader(conn.tcpConn, conn.perf.NetRead)
	} else {
		conn.ccr = conn.tcpConn
	}

	if conn.perf.NetWrite != nil {
		conn.ccw = NewCountedWriter(conn.tcpConn, conn.perf.NetWrite)
	} else {
		conn.ccw = conn.tcpConn
	}

	greeting := make([]byte, 128)

	connectDeadline := time.Now().Add(opts.ConnectTimeout)
	conn.tcpConn.SetDeadline(connectDeadline)
	// removing deadline deferred
	defer conn.tcpConn.SetDeadline(time.Time{})

	_, err = io.ReadFull(conn.ccr, greeting)
	if err != nil {
		return
	}

	conn.greeting = &Greeting{
		Version: greeting[:64],
		Auth:    greeting[64:108],
	}

	// try to authenticate if user have been provided
	if len(opts.User) > 0 {
		requestID := conn.nextID()

		pp := packetPool.GetWithID(requestID)

		err = pp.packMsg(&Auth{
			User:         opts.User,
			Password:     opts.Password,
			GreetingAuth: conn.greeting.Auth,
		}, conn.packData)
		if err != nil {
			pp.Release()
			return
		}

		_, err = pp.WriteTo(conn.ccw)
		pp.Release()
		if err != nil {
			return
		}

		pp = packetPool.Get()
		defer pp.Release()

		if err = pp.readPacket(conn.ccr); err != nil {
			return
		}

		authResponse := &pp.packet
		if authResponse.requestID != requestID {
			err = ErrSyncFailed
			return
		}

		if authResponse.Result != nil && authResponse.Result.Error != nil {
			err = authResponse.Result.Error
			return
		}
	}

	return
}

func parseOptions(dsnString string, options *Options) (*url.URL, Options, error) {
	if options == nil {
		options = &Options{}
	}
	opts := *options // copy to new object

	// remove schema, if present

	// === for backward compatibility (only tcp despite of user wishes :)
	dsnString = strings.TrimPrefix(dsnString, "unix:")
	// ===

	// tcp is the default scheme
	switch {
	case strings.HasPrefix(dsnString, "tcp://"):
	case strings.HasPrefix(dsnString, "//"):
		dsnString = "tcp:" + dsnString
	default:
		dsnString = "tcp://" + dsnString
	}
	dsn, err := url.Parse(dsnString)
	if err != nil {
		return dsn, opts, err
	}

	if opts.ConnectTimeout.Nanoseconds() == 0 {
		opts.ConnectTimeout = DefaultConnectTimeout
	}
	if opts.QueryTimeout.Nanoseconds() == 0 {
		opts.QueryTimeout = DefaultQueryTimeout
	}

	if len(opts.User) == 0 {
		if user := dsn.User; user != nil {
			opts.User = user.Username()
			opts.Password, _ = user.Password()
		}
	}

	if len(opts.DefaultSpace) == 0 && len(dsn.Path) > 0 {
		path := strings.TrimPrefix(dsn.Path, "/")
		// check it if it is necessary
		switch {
		case len(path) == 0:
			return nil, opts, ErrEmptyDefaultSpace
		//case strings.IndexAny(path, "/ ,") != -1:
		//	return nil, opts, ErrBadDSNPath
		default:
			opts.DefaultSpace = path
		}
	}

	return dsn, opts, nil
}

func (conn *Connection) pullSchema() (err error) {
	// select space and index schema
	request := func(q Query) (*Result, error) {
		var err error

		requestID := conn.nextID()

		pp := packetPool.GetWithID(requestID)
		if err = pp.packMsg(q, conn.packData); err != nil {
			pp.Release()
			return nil, err
		}

		_, err = pp.WriteTo(conn.ccw)
		pp.Release()
		if err != nil {
			return nil, err
		}

		pp = packetPool.Get()
		defer pp.Release()

		if err = pp.readPacket(conn.ccr); err != nil {
			return nil, err
		}

		response := &pp.packet
		if response.requestID != requestID {
			return nil, errors.New("Bad response requestID")
		}

		if response.Result == nil {
			return nil, errors.New("Nil response result")
		}

		if response.Result.Error != nil {
			return nil, response.Result.Error
		}

		return response.Result, nil
	}

	res, err := request(&Select{
		Space:    ViewSpace,
		Key:      0,
		Iterator: IterAll,
	})
	if err != nil {
		return
	}

	for _, space := range res.Data {
		spaceID, _ := conn.packData.spaceNo(space[0])
		conn.packData.spaceMap[space[2].(string)] = spaceID
	}

	res, err = request(&Select{
		Space:    ViewIndex,
		Key:      0,
		Iterator: IterAll,
	})
	if err != nil {
		return
	}

	for _, index := range res.Data {
		spaceID, _ := conn.packData.fieldNo(index[0])
		indexID, _ := conn.packData.fieldNo(index[1])
		indexName := index[2].(string)
		indexAttr := index[4].(map[string]interface{}) // e.g: {"unique": true}
		indexFields := index[5].([]interface{})        // e.g: [[0 num] [1 str]]

		indexSpaceMap, exists := conn.packData.indexMap[spaceID]
		if !exists {
			indexSpaceMap = make(map[string]uint64)
			conn.packData.indexMap[spaceID] = indexSpaceMap
		}
		indexSpaceMap[indexName] = indexID

		// build list of primary key field numbers for this space, if the PK is detected
		if indexAttr != nil && indexID == 0 {
			if unique, ok := indexAttr["unique"]; ok && unique.(bool) {
				pk := make([]int, len(indexFields))
				for i := range indexFields {
					descr := indexFields[i].([]interface{})
					f, _ := conn.packData.fieldNo(descr[0])
					pk[i] = int(f)
				}
				conn.packData.primaryKeyMap[spaceID] = pk
			}
		}
	}

	return
}

func (conn *Connection) nextID() uint64 {
	return atomic.AddUint64(&conn.requestID, 1)
}

func (conn *Connection) stop() {
	conn.closeOnce.Do(func() {
		// debug.PrintStack()
		close(conn.exit)
		conn.tcpConn.Close()
		runtime.GC()
	})
}

func (conn *Connection) GetPrimaryKeyFields(space interface{}) ([]int, bool) {
	var spaceID uint64
	var err error

	if conn.packData == nil {
		return nil, false
	}
	if spaceID, err = conn.packData.spaceNo(space); err != nil {
		return nil, false
	}

	f, ok := conn.packData.primaryKeyMap[spaceID]
	return f, ok
}

func (conn *Connection) Close() {
	conn.stop()
	<-conn.closed
}

func (conn *Connection) String() string {
	return conn.remoteAddr
}

func (conn *Connection) IsClosed() bool {
	select {
	case <-conn.exit:
		return true
	default:
		return false
	}
}

func (conn *Connection) getError() error {
	conn.firstErrorLock.Lock()
	defer conn.firstErrorLock.Unlock()
	return conn.firstError
}

func (conn *Connection) setError(err error) {
	if err != nil && err != io.EOF {
		conn.firstErrorLock.Lock()
		if conn.firstError == nil {
			conn.firstError = err
		}
		conn.firstErrorLock.Unlock()
	}
}

func (conn *Connection) worker() {
	var wg sync.WaitGroup

	wg.Add(2)

	go func() {
		err := conn.writer()
		conn.setError(err)
		conn.stop()
		wg.Done()
	}()

	go func() {
		err := conn.reader()
		conn.setError(err)
		conn.stop()
		wg.Done()
	}()

	wg.Wait()

	// release all pending packets
	writeChan := conn.writeChan
	conn.writeChan = nil

CLEANUP_LOOP:
	for {
		select {
		case pp := <-writeChan:
			pp.Release()
		default:
			break CLEANUP_LOOP
		}
	}

	// send error reply to all pending requests
	conn.requests.CleanUp(func(req *request) {
		select {
		case req.replyChan <- &AsyncResult{
			Error:     ConnectionClosedError(conn),
			ErrorCode: ErrNoConnection,
			Opaque:    req.opaque,
		}:
		default:
		}
	})

	close(conn.closed)
}

func (conn *Connection) writer() (err error) {
	writeChan := conn.writeChan
	stopChan := conn.exit
	w := bufio.NewWriterSize(conn.ccw, DefaultWriterBufSize)

	wp := func(w io.Writer, packet *BinaryPacket) error {
		if conn.perf.NetPacketsOut != nil {
			conn.perf.NetPacketsOut.Add(1)
		}
		_, err := packet.WriteTo(w)
		packet.Release()
		return err
	}

WRITER_LOOP:
	for {
		select {
		case packet, ok := <-writeChan:
			if !ok {
				break WRITER_LOOP
			}
			if err = wp(w, packet); err != nil {
				break WRITER_LOOP
			}
		case <-stopChan:
			break WRITER_LOOP
		default:
			if err = w.Flush(); err != nil {
				break WRITER_LOOP
			}

			// same without flush
			select {
			case packet, ok := <-writeChan:
				if !ok {
					break WRITER_LOOP
				}
				if err = wp(w, packet); err != nil {
					break WRITER_LOOP
				}
			case <-stopChan:
				break WRITER_LOOP
			}
		}
	}

	return
}

func (conn *Connection) reader() (err error) {
	var pp *BinaryPacket
	var requestID uint64

	r := bufio.NewReaderSize(conn.ccr, DefaultReaderBufSize)

READER_LOOP:
	for {
		pp := packetPool.Get()
		if requestID, err = pp.readRawPacket(r); err != nil {
			break READER_LOOP
		}

		if conn.perf.NetPacketsIn != nil {
			conn.perf.NetPacketsIn.Add(1)
		}

		req := conn.requests.Pop(requestID)
		if req == nil {
			pp.Release()
			pp = nil
			continue
		}

		select {
		case req.replyChan <- &AsyncResult{0, nil, pp, conn, req.opaque}:
			pp = nil
		default:
		}
	}

	if pp != nil {
		pp.Release()
	}
	return
}
<|endoftext|>


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

package zanzibar

import (
	"net"
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/uber-go/zap"
)

// HTTPServer like, http.Server but improved management of listening and serving
// Allows you to listen on port 0, query for real OS port and then serve requests
type HTTPServer struct {
	*http.Server
	Logger zap.Logger

	listeningSocket net.Listener
	closing         bool

	RealPort int32
	RealAddr string
}

// JustListen will only listen on port and query real addr
func (server *HTTPServer) JustListen() (net.Listener, error) {
	addr := server.Addr
	if addr == "" {
		/* coverage ignore next line */
		addr = ":http"
	}

	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return nil, err
	}
	server.listeningSocket = ln

	realAddr := ln.Addr().(*net.TCPAddr)
	server.RealPort = int32(realAddr.Port)
	server.RealAddr = realAddr.IP.String() + ":" +
		strconv.Itoa(int(server.RealPort))

	return ln, nil
}

// JustServe will serve all incoming requests
func (server *HTTPServer) JustServe(waitGroup *sync.WaitGroup) {
	ln := server.listeningSocket.(*net.TCPListener)

	err := server.Serve(tcpKeepAliveListener{ln})
	if err != nil && !server.closing {
		/* coverage ignore next line */
		server.Logger.Error("Error http serving",
			zap.String("error", err.Error()),
		)
	}

	waitGroup.Done()
}

// Close the listening socket
func (server *HTTPServer) Close() {
	server.closing = true
	if server.listeningSocket == nil {
		return
	}

	err := server.listeningSocket.Close()
	if err != nil {
		/* coverage ignore next line */
		server.Logger.Error("Error closing listening socket",
			zap.String("error", err.Error()),
		)
	}
}

// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	_ = tc.SetKeepAlive(true)
	_ = tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}
<commit_msg>add another coverage ignore for 1.8<commit_after>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zanzibar

import (
	"net"
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/uber-go/zap"
)

// HTTPServer like, http.Server but improved management of listening and serving
// Allows you to listen on port 0, query for real OS port and then serve requests
type HTTPServer struct {
	*http.Server
	Logger zap.Logger

	listeningSocket net.Listener
	closing         bool

	RealPort int32
	RealAddr string
}

// JustListen will only listen on port and query real addr
func (server *HTTPServer) JustListen() (net.Listener, error) {
	addr := server.Addr
	if addr == "" {
		/* coverage ignore next line */
		addr = ":http"
	}

	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return nil, err
	}
	server.listeningSocket = ln

	realAddr := ln.Addr().(*net.TCPAddr)
	server.RealPort = int32(realAddr.Port)
	server.RealAddr = realAddr.IP.String() + ":" +
		strconv.Itoa(int(server.RealPort))

	return ln, nil
}

// JustServe will serve all incoming requests
func (server *HTTPServer) JustServe(waitGroup *sync.WaitGroup) {
	ln := server.listeningSocket.(*net.TCPListener)

	err := server.Serve(tcpKeepAliveListener{ln})
	if err != nil && !server.closing {
		/* coverage ignore next line */
		server.Logger.Error("Error http serving",
			zap.String("error", err.Error()),
		)
	}

	waitGroup.Done()
}

// Close the listening socket
func (server *HTTPServer) Close() {
	server.closing = true
	if server.listeningSocket == nil {
		/* coverage ignore next line */
		return
	}

	err := server.listeningSocket.Close()
	if err != nil {
		/* coverage ignore next line */
		server.Logger.Error("Error closing listening socket",
			zap.String("error", err.Error()),
		)
	}
}

// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	_ = tc.SetKeepAlive(true)
	_ = tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}
<|endoftext|>


<commit_before>package softlayer

import (
	"errors"
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/packer"
	"log"
	"os"
	"time"
)

// The unique ID for this builder.
const BuilderId = "packer.softlayer"

type config struct {
	common.PackerConfig `mapstructure:",squash"`

	Username         string `mapstructure:"username"`
	APIKey           string `mapstructure:"api_key"`
	DatacenterName   string `mapstructure:"datacenter_name"`
	ImageName        string `mapstructure:"image_name"`
	ImageDescription string `mapstructure:"image_description"`
	BaseImageId      string `mapstructure:"base_image_id"`
	BaseOsCode       string `mapstructure:"base_os_code"`

	InstanceName         string `mapstructure:"instance_name"`
	InstanceDomain       string `mapstructure:"instance_domain"`
	InstanceCpu          int    `mapstructure:"instance_cpu"`
	InstanceMemory       int64  `mapstructure:"instance_memory"`
	InstanceNetworkSpeed int    `mapstructure:"instance_network_speed"`
	InstanceDiskCapacity int    `mapstructure:"instance_disk_capacity"`
	SshPort              int64  `mapstructure:"ssh_port"`
	SshUserName          string `mapstructure:"ssh_username"`
	SshPrivateKeyFile    string `mapstructure:"ssh_private_key_file"`

	RawSshTimeout   string `mapstructure:"ssh_timeout"`
	RawStateTimeout string `mapstructure:"instance_state_timeout"`

	SshTimeout   time.Duration
	StateTimeout time.Duration

	tpl *packer.ConfigTemplate
}

// Builder represents a Packer Builder.
type Builder struct {
	config config
	runner multistep.Runner
}

// Prepare processes the build configuration parameters.
func (self *Builder) Prepare(raws ...interface{}) (parms []string, retErr error) {
	metadata, err := common.DecodeConfig(&self.config, raws...)
	if err != nil {
		return nil, err
	}

	// Check that there aren't any unknown configuration keys defined
	errs := common.CheckUnusedConfig(metadata)
	if errs == nil {
		errs = &packer.MultiError{}
	}

	self.config.tpl, err = packer.NewConfigTemplate()
	if err != nil {
		return nil, err
	}

	// Assign default values if possible
	if self.config.APIKey == "" {
		// Default to environment variable for api_key, if it exists
		self.config.APIKey = os.Getenv("SOFTLAYER_API_KEY")
	}

	if self.config.Username == "" {
		// Default to environment variable for client_id, if it exists
		self.config.Username = os.Getenv("SOFTLAYER_USER_NAME")
	}

	if self.config.DatacenterName == "" {
		self.config.DatacenterName = "ams01"
	}

	if self.config.InstanceName == "" {
		self.config.InstanceName = fmt.Sprintf("packer-softlayer-%s", time.Now().Unix())
	}

	if self.config.InstanceDomain == "" {
		self.config.InstanceDomain = "defaultdomain.com"
	}

	if self.config.ImageDescription == "" {
		self.config.ImageDescription = "Instance snapshot. Generated by packer.io"
	}

	if self.config.InstanceCpu == 0 {
		self.config.InstanceCpu = 1
	}

	if self.config.InstanceMemory == 0 {
		self.config.InstanceMemory = 1024
	}

	if self.config.InstanceNetworkSpeed == 0 {
		self.config.InstanceNetworkSpeed = 10
	}

	if self.config.InstanceDiskCapacity == 0 {
		self.config.InstanceDiskCapacity = 25
	}

	if self.config.SshPort == 0 {
		self.config.SshPort = 22
	}

	if self.config.SshUserName == "" {
		self.config.SshUserName = "root"
	}

	if self.config.RawSshTimeout == "" {
		self.config.RawSshTimeout = "5m"
	}

	if self.config.RawStateTimeout == "" {
		self.config.RawStateTimeout = "10m"
	}

	templates := map[string]*string{
		"username":               &self.config.Username,
		"api_key":                &self.config.APIKey,
		"datacenter_name":        &self.config.DatacenterName,
		"base_image_id":          &self.config.BaseImageId,
		"image_name":             &self.config.ImageName,
		"image_description":      &self.config.ImageDescription,
		"base_os_code":           &self.config.BaseOsCode,
		"instance_name":          &self.config.InstanceName,
		"instance_domain":        &self.config.InstanceDomain,
		"ssh_timeout":            &self.config.RawSshTimeout,
		"instance_state_timeout": &self.config.RawStateTimeout,
		"ssh_username":           &self.config.SshUserName,
		"ssh_private_key_file":   &self.config.SshPrivateKeyFile,
	}

	for n, ptr := range templates {
		var err error
		*ptr, err = self.config.tpl.Process(*ptr, nil)
		if err != nil {
			errs = packer.MultiErrorAppend(errs, fmt.Errorf("Error processing %s: %s", n, err))
		}
	}

	// Check for required configurations that will display errors if not set
	if self.config.APIKey == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("api_key or the SOFTLAYER_API_KEY environment variable must be specified"))
	}

	if self.config.Username == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("username or the SOFTLAYER_USER_NAME environment variable must be specified"))
	}

	if self.config.ImageName == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("a image_name must be specified"))
	}

	if self.config.BaseImageId == "" && self.config.BaseOsCode == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("please specify base_image_id or base_os_code"))
	}

	if self.config.BaseImageId != "" && self.config.BaseOsCode != "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("please specify only one of base_image_id or base_os_code"))
	}

	if self.config.BaseImageId != "" && self.config.SshPrivateKeyFile == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("when using base_image_id, you must specify ssh_private_key_file "+
				"since automatic ssh key config for custom images isn't supported by SoftLayer API"))
	}

	// Translate date configuration data from string to time format
	sshTimeout, err := time.ParseDuration(self.config.RawSshTimeout)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing ssh_timeout: %s", err))
	}
	self.config.SshTimeout = sshTimeout

	stateTimeout, err := time.ParseDuration(self.config.RawStateTimeout)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing state_timeout: %s", err))
	}
	self.config.StateTimeout = stateTimeout

	log.Println(common.ScrubConfig(self.config, self.config.APIKey, self.config.Username))

	if len(errs.Errors) > 0 {
		retErr = errors.New(errs.Error())
	}

	return nil, retErr
}

// Run executes a SoftLayer Packer build and returns a packer.Artifact
// representing a SoftLayer machine image (flex).
func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {

	// Create the client
	client := SoftlayerClient{}.New(self.config.Username, self.config.APIKey)

	// Set up the state which is used to share state between the steps
	state := new(multistep.BasicStateBag)
	state.Put("config", self.config)
	state.Put("client", client)
	state.Put("hook", hook)
	state.Put("ui", ui)

	// Build the steps
	steps := []multistep.Step{
		&stepCreateSshKey{
			PrivateKeyFile: self.config.SshPrivateKeyFile,
		},
		new(stepCreateInstance),
		new(stepWaitforInstance),
		&common.StepConnectSSH{
			SSHAddress:     sshAddress,
			SSHConfig:      sshConfig,
			SSHWaitTimeout: self.config.SshTimeout,
		},
		new(common.StepProvision),
		new(stepCaptureImage),
	}

	// Create the runner which will run the steps we just build
	self.runner = &multistep.BasicRunner{Steps: steps}
	self.runner.Run(state)

	// If there was an error, return that
	if rawErr, ok := state.GetOk("error"); ok {
		return nil, rawErr.(error)
	}

	if _, ok := state.GetOk("image_id"); !ok {
		log.Println("Failed to find image_id in state. Bug?")
		return nil, nil
	}

	// Create an artifact and return it
	artifact := &Artifact{
		imageName:      self.config.ImageName,
		imageId:        state.Get("image_id").(string),
		datacenterName: self.config.DatacenterName,
		client:         client,
	}

	return artifact, nil
}

// Cancel.
func (self *Builder) Cancel() {
	if self.runner != nil {
		log.Println("Cancelling the step runner...")
		self.runner.Cancel()
	}
	fmt.Println("Canceling the builder")
}
<commit_msg>Enable user variables in packer configuration templates.<commit_after>package softlayer

import (
	"errors"
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/packer"
	"log"
	"os"
	"time"
)

// The unique ID for this builder.
const BuilderId = "packer.softlayer"

type config struct {
	common.PackerConfig `mapstructure:",squash"`

	Username         string `mapstructure:"username"`
	APIKey           string `mapstructure:"api_key"`
	DatacenterName   string `mapstructure:"datacenter_name"`
	ImageName        string `mapstructure:"image_name"`
	ImageDescription string `mapstructure:"image_description"`
	BaseImageId      string `mapstructure:"base_image_id"`
	BaseOsCode       string `mapstructure:"base_os_code"`

	InstanceName         string `mapstructure:"instance_name"`
	InstanceDomain       string `mapstructure:"instance_domain"`
	InstanceCpu          int    `mapstructure:"instance_cpu"`
	InstanceMemory       int64  `mapstructure:"instance_memory"`
	InstanceNetworkSpeed int    `mapstructure:"instance_network_speed"`
	InstanceDiskCapacity int    `mapstructure:"instance_disk_capacity"`
	SshPort              int64  `mapstructure:"ssh_port"`
	SshUserName          string `mapstructure:"ssh_username"`
	SshPrivateKeyFile    string `mapstructure:"ssh_private_key_file"`

	RawSshTimeout   string `mapstructure:"ssh_timeout"`
	RawStateTimeout string `mapstructure:"instance_state_timeout"`

	SshTimeout   time.Duration
	StateTimeout time.Duration

	tpl *packer.ConfigTemplate
}

// Builder represents a Packer Builder.
type Builder struct {
	config config
	runner multistep.Runner
}

// Prepare processes the build configuration parameters.
func (self *Builder) Prepare(raws ...interface{}) (parms []string, retErr error) {
	metadata, err := common.DecodeConfig(&self.config, raws...)
	if err != nil {
		return nil, err
	}

	// Check that there aren't any unknown configuration keys defined
	errs := common.CheckUnusedConfig(metadata)
	if errs == nil {
		errs = &packer.MultiError{}
	}

	self.config.tpl, err = packer.NewConfigTemplate()
	if err != nil {
		return nil, err
	}
	self.config.tpl.UserVars = self.config.PackerUserVars

	// Assign default values if possible
	if self.config.APIKey == "" {
		// Default to environment variable for api_key, if it exists
		self.config.APIKey = os.Getenv("SOFTLAYER_API_KEY")
	}

	if self.config.Username == "" {
		// Default to environment variable for client_id, if it exists
		self.config.Username = os.Getenv("SOFTLAYER_USER_NAME")
	}

	if self.config.DatacenterName == "" {
		self.config.DatacenterName = "ams01"
	}

	if self.config.InstanceName == "" {
		self.config.InstanceName = fmt.Sprintf("packer-softlayer-%s", time.Now().Unix())
	}

	if self.config.InstanceDomain == "" {
		self.config.InstanceDomain = "defaultdomain.com"
	}

	if self.config.ImageDescription == "" {
		self.config.ImageDescription = "Instance snapshot. Generated by packer.io"
	}

	if self.config.InstanceCpu == 0 {
		self.config.InstanceCpu = 1
	}

	if self.config.InstanceMemory == 0 {
		self.config.InstanceMemory = 1024
	}

	if self.config.InstanceNetworkSpeed == 0 {
		self.config.InstanceNetworkSpeed = 10
	}

	if self.config.InstanceDiskCapacity == 0 {
		self.config.InstanceDiskCapacity = 25
	}

	if self.config.SshPort == 0 {
		self.config.SshPort = 22
	}

	if self.config.SshUserName == "" {
		self.config.SshUserName = "root"
	}

	if self.config.RawSshTimeout == "" {
		self.config.RawSshTimeout = "5m"
	}

	if self.config.RawStateTimeout == "" {
		self.config.RawStateTimeout = "10m"
	}

	templates := map[string]*string{
		"username":               &self.config.Username,
		"api_key":                &self.config.APIKey,
		"datacenter_name":        &self.config.DatacenterName,
		"base_image_id":          &self.config.BaseImageId,
		"image_name":             &self.config.ImageName,
		"image_description":      &self.config.ImageDescription,
		"base_os_code":           &self.config.BaseOsCode,
		"instance_name":          &self.config.InstanceName,
		"instance_domain":        &self.config.InstanceDomain,
		"ssh_timeout":            &self.config.RawSshTimeout,
		"instance_state_timeout": &self.config.RawStateTimeout,
		"ssh_username":           &self.config.SshUserName,
		"ssh_private_key_file":   &self.config.SshPrivateKeyFile,
	}

	for n, ptr := range templates {
		var err error
		*ptr, err = self.config.tpl.Process(*ptr, nil)
		if err != nil {
			errs = packer.MultiErrorAppend(errs, fmt.Errorf("Error processing %s: %s", n, err))
		}
	}

	// Check for required configurations that will display errors if not set
	if self.config.APIKey == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("api_key or the SOFTLAYER_API_KEY environment variable must be specified"))
	}

	if self.config.Username == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("username or the SOFTLAYER_USER_NAME environment variable must be specified"))
	}

	if self.config.ImageName == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("a image_name must be specified"))
	}

	if self.config.BaseImageId == "" && self.config.BaseOsCode == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("please specify base_image_id or base_os_code"))
	}

	if self.config.BaseImageId != "" && self.config.BaseOsCode != "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("please specify only one of base_image_id or base_os_code"))
	}

	if self.config.BaseImageId != "" && self.config.SshPrivateKeyFile == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("when using base_image_id, you must specify ssh_private_key_file "+
				"since automatic ssh key config for custom images isn't supported by SoftLayer API"))
	}

	// Translate date configuration data from string to time format
	sshTimeout, err := time.ParseDuration(self.config.RawSshTimeout)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing ssh_timeout: %s", err))
	}
	self.config.SshTimeout = sshTimeout

	stateTimeout, err := time.ParseDuration(self.config.RawStateTimeout)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing state_timeout: %s", err))
	}
	self.config.StateTimeout = stateTimeout

	log.Println(common.ScrubConfig(self.config, self.config.APIKey, self.config.Username))

	if len(errs.Errors) > 0 {
		retErr = errors.New(errs.Error())
	}

	return nil, retErr
}

// Run executes a SoftLayer Packer build and returns a packer.Artifact
// representing a SoftLayer machine image (flex).
func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {

	// Create the client
	client := SoftlayerClient{}.New(self.config.Username, self.config.APIKey)

	// Set up the state which is used to share state between the steps
	state := new(multistep.BasicStateBag)
	state.Put("config", self.config)
	state.Put("client", client)
	state.Put("hook", hook)
	state.Put("ui", ui)

	// Build the steps
	steps := []multistep.Step{
		&stepCreateSshKey{
			PrivateKeyFile: self.config.SshPrivateKeyFile,
		},
		new(stepCreateInstance),
		new(stepWaitforInstance),
		&common.StepConnectSSH{
			SSHAddress:     sshAddress,
			SSHConfig:      sshConfig,
			SSHWaitTimeout: self.config.SshTimeout,
		},
		new(common.StepProvision),
		new(stepCaptureImage),
	}

	// Create the runner which will run the steps we just build
	self.runner = &multistep.BasicRunner{Steps: steps}
	self.runner.Run(state)

	// If there was an error, return that
	if rawErr, ok := state.GetOk("error"); ok {
		return nil, rawErr.(error)
	}

	if _, ok := state.GetOk("image_id"); !ok {
		log.Println("Failed to find image_id in state. Bug?")
		return nil, nil
	}

	// Create an artifact and return it
	artifact := &Artifact{
		imageName:      self.config.ImageName,
		imageId:        state.Get("image_id").(string),
		datacenterName: self.config.DatacenterName,
		client:         client,
	}

	return artifact, nil
}

// Cancel.
func (self *Builder) Cancel() {
	if self.runner != nil {
		log.Println("Cancelling the step runner...")
		self.runner.Cancel()
	}
	fmt.Println("Canceling the builder")
}
<|endoftext|>


<commit_before>package consul

import (
	"crypto/tls"
	"fmt"
	"github.com/armon/go-metrics"
	"github.com/hashicorp/consul/consul/structs"
	"github.com/inconshreveable/muxado"
	"github.com/ugorji/go/codec"
	"io"
	"math/rand"
	"net"
	"strings"
	"time"
)

type RPCType byte

const (
	rpcConsul RPCType = iota
	rpcRaft
	rpcMultiplex
	rpcTLS
)

const (
	maxQueryTime = 600 * time.Second
)

// listen is used to listen for incoming RPC connections
func (s *Server) listen() {
	for {
		// Accept a connection
		conn, err := s.rpcListener.Accept()
		if err != nil {
			if s.shutdown {
				return
			}
			s.logger.Printf("[ERR] consul.rpc: failed to accept RPC conn: %v", err)
			continue
		}

		// Track this client
		s.rpcClientLock.Lock()
		s.rpcClients[conn] = struct{}{}
		s.rpcClientLock.Unlock()

		go s.handleConn(conn)
		metrics.IncrCounter([]string{"consul", "rpc", "accept_conn"}, 1)
	}
}

// handleConn is used to determine if this is a Raft or
// Consul type RPC connection and invoke the correct handler
func (s *Server) handleConn(conn net.Conn) {
	// Read a single byte
	buf := make([]byte, 1)
	if _, err := conn.Read(buf); err != nil {
		s.logger.Printf("[ERR] consul.rpc: failed to read byte: %v", err)
		conn.Close()
		return
	}

	// Switch on the byte
	switch RPCType(buf[0]) {
	case rpcConsul:
		s.handleConsulConn(conn)

	case rpcRaft:
		metrics.IncrCounter([]string{"consul", "rpc", "raft_handoff"}, 1)
		s.raftLayer.Handoff(conn)

	case rpcMultiplex:
		s.handleMultiplex(conn)

	case rpcTLS:
		if s.rpcTLS == nil {
			s.logger.Printf("[WARN] consul.rpc: TLS connection attempted, server not configured for TLS")
			conn.Close()
			return
		}
		conn = tls.Server(conn, s.rpcTLS)
		s.handleConn(conn)

	default:
		s.logger.Printf("[ERR] consul.rpc: unrecognized RPC byte: %v", buf[0])
		conn.Close()
		return
	}
}

// handleMultiplex is used to multiplex a single incoming connection
func (s *Server) handleMultiplex(conn net.Conn) {
	defer conn.Close()
	server := muxado.Server(conn)
	for {
		sub, err := server.Accept()
		if err != nil {
			if !strings.Contains(err.Error(), "closed") {
				s.logger.Printf("[ERR] consul.rpc: multiplex conn accept failed: %v", err)
			}
			return
		}
		go s.handleConsulConn(sub)
	}
}

// handleConsulConn is used to service a single Consul RPC connection
func (s *Server) handleConsulConn(conn net.Conn) {
	defer func() {
		conn.Close()
		s.rpcClientLock.Lock()
		delete(s.rpcClients, conn)
		s.rpcClientLock.Unlock()
	}()

	rpcCodec := codec.GoRpc.ServerCodec(conn, &codec.MsgpackHandle{})
	for !s.shutdown {
		if err := s.rpcServer.ServeRequest(rpcCodec); err != nil {
			if err != io.EOF {
				s.logger.Printf("[ERR] consul.rpc: RPC error: %v (%v)", err, conn)
			}
			return
		}
	}
}

// forward is used to forward to a remote DC or to forward to the local leader
// Returns a bool of if forwarding was performed, as well as any error
func (s *Server) forward(method, dc string, args interface{}, reply interface{}) (bool, error) {
	// Handle DC forwarding
	if dc != s.config.Datacenter {
		err := s.forwardDC(method, dc, args, reply)
		return true, err
	}

	// Handle leader forwarding
	if !s.IsLeader() {
		err := s.forwardLeader(method, args, reply)
		return true, err
	}
	return false, nil
}

// forwardLeader is used to forward an RPC call to the leader, or fail if no leader
func (s *Server) forwardLeader(method string, args interface{}, reply interface{}) error {
	leader := s.raft.Leader()
	if leader == nil {
		return structs.ErrNoLeader
	}
	return s.connPool.RPC(leader, method, args, reply)
}

// forwardDC is used to forward an RPC call to a remote DC, or fail if no servers
func (s *Server) forwardDC(method, dc string, args interface{}, reply interface{}) error {
	// Bail if we can't find any servers
	s.remoteLock.RLock()
	servers := s.remoteConsuls[dc]
	if len(servers) == 0 {
		s.remoteLock.RUnlock()
		s.logger.Printf("[WARN] consul.rpc: RPC request for DC '%s', no path found", dc)
		return structs.ErrNoDCPath
	}

	// Select a random addr
	offset := rand.Int31() % int32(len(servers))
	server := servers[offset]
	s.remoteLock.RUnlock()

	// Forward to remote Consul
	metrics.IncrCounter([]string{"consul", "rpc", "cross-dc", dc}, 1)
	return s.connPool.RPC(server, method, args, reply)
}

// raftApply is used to encode a message, run it through raft, and return
// the FSM response along with any errors
func (s *Server) raftApply(t structs.MessageType, msg interface{}) (interface{}, error) {
	buf, err := structs.Encode(t, msg)
	if err != nil {
		return nil, fmt.Errorf("Failed to encode request: %v", err)
	}

	future := s.raft.Apply(buf, 0)
	if err := future.Error(); err != nil {
		return nil, err
	}

	return future.Response(), nil
}

// blockingRPC is used for queries that need to wait for a
// minimum index. This is used to block and wait for changes.
func (s *Server) blockingRPC(b *structs.BlockingQuery, tables MDBTables, run func() (uint64, error)) error {
	var timeout <-chan time.Time
	var notifyCh chan struct{}

	// Fast path non-blocking
	if b.MinQueryIndex == 0 {
		goto RUN_QUERY
	}

	// Sanity check that we have tables to block on
	if len(tables) == 0 {
		panic("no tables to block on")
	}

	// Restrict the max query time
	if b.MaxQueryTime > maxQueryTime {
		b.MaxQueryTime = maxQueryTime
	}

	// Ensure a time limit is set if we have an index
	if b.MinQueryIndex > 0 && b.MaxQueryTime == 0 {
		b.MaxQueryTime = maxQueryTime
	}

	// Setup a query timeout
	if b.MaxQueryTime > 0 {
		timeout = time.After(b.MaxQueryTime)
	}

	// Setup a notification channel for changes
SETUP_NOTIFY:
	if b.MinQueryIndex > 0 {
		notifyCh = make(chan struct{}, 1)
		s.fsm.State().Watch(tables, notifyCh)
	}

	// Run the query function
RUN_QUERY:
	idx, err := run()

	// Check for minimum query time
	if err == nil && idx <= b.MinQueryIndex {
		select {
		case <-notifyCh:
			goto SETUP_NOTIFY
		case <-timeout:
		}
	}
	return err
}
<commit_msg>consul: Enforce TLS if VerifyIncoming is set<commit_after>package consul

import (
	"crypto/tls"
	"fmt"
	"github.com/armon/go-metrics"
	"github.com/hashicorp/consul/consul/structs"
	"github.com/inconshreveable/muxado"
	"github.com/ugorji/go/codec"
	"io"
	"math/rand"
	"net"
	"strings"
	"time"
)

type RPCType byte

const (
	rpcConsul RPCType = iota
	rpcRaft
	rpcMultiplex
	rpcTLS
)

const (
	maxQueryTime = 600 * time.Second
)

// listen is used to listen for incoming RPC connections
func (s *Server) listen() {
	for {
		// Accept a connection
		conn, err := s.rpcListener.Accept()
		if err != nil {
			if s.shutdown {
				return
			}
			s.logger.Printf("[ERR] consul.rpc: failed to accept RPC conn: %v", err)
			continue
		}

		// Track this client
		s.rpcClientLock.Lock()
		s.rpcClients[conn] = struct{}{}
		s.rpcClientLock.Unlock()

		go s.handleConn(conn)
		metrics.IncrCounter([]string{"consul", "rpc", "accept_conn"}, 1)
	}
}

// handleConn is used to determine if this is a Raft or
// Consul type RPC connection and invoke the correct handler
func (s *Server) handleConn(conn net.Conn) {
	// Read a single byte
	buf := make([]byte, 1)
	if _, err := conn.Read(buf); err != nil {
		s.logger.Printf("[ERR] consul.rpc: failed to read byte: %v", err)
		conn.Close()
		return
	}

	// Check if entering TLS mode
	isTLS := false
	if RPCType(buf[0]) == rpcTLS {
		if s.rpcTLS == nil {
			s.logger.Printf("[WARN] consul.rpc: TLS connection attempted, server not configured for TLS")
			conn.Close()
			return
		}
		conn = tls.Server(conn, s.rpcTLS)
		isTLS = true
	}

	// Enforce TLS if VerifyIncoming is set
	if s.config.VerifyIncoming && !isTLS {
		s.logger.Printf("[WARN] consul.rpc: Non-TLS connection attempted with VerifyIncoming set")
		conn.Close()
		return
	}

	// Switch on the byte
	switch RPCType(buf[0]) {
	case rpcConsul:
		s.handleConsulConn(conn)

	case rpcRaft:
		metrics.IncrCounter([]string{"consul", "rpc", "raft_handoff"}, 1)
		s.raftLayer.Handoff(conn)

	case rpcMultiplex:
		s.handleMultiplex(conn)

	default:
		s.logger.Printf("[ERR] consul.rpc: unrecognized RPC byte: %v", buf[0])
		conn.Close()
		return
	}
}

// handleMultiplex is used to multiplex a single incoming connection
func (s *Server) handleMultiplex(conn net.Conn) {
	defer conn.Close()
	server := muxado.Server(conn)
	for {
		sub, err := server.Accept()
		if err != nil {
			if !strings.Contains(err.Error(), "closed") {
				s.logger.Printf("[ERR] consul.rpc: multiplex conn accept failed: %v", err)
			}
			return
		}
		go s.handleConsulConn(sub)
	}
}

// handleConsulConn is used to service a single Consul RPC connection
func (s *Server) handleConsulConn(conn net.Conn) {
	defer func() {
		conn.Close()
		s.rpcClientLock.Lock()
		delete(s.rpcClients, conn)
		s.rpcClientLock.Unlock()
	}()

	rpcCodec := codec.GoRpc.ServerCodec(conn, &codec.MsgpackHandle{})
	for !s.shutdown {
		if err := s.rpcServer.ServeRequest(rpcCodec); err != nil {
			if err != io.EOF {
				s.logger.Printf("[ERR] consul.rpc: RPC error: %v (%v)", err, conn)
			}
			return
		}
	}
}

// forward is used to forward to a remote DC or to forward to the local leader
// Returns a bool of if forwarding was performed, as well as any error
func (s *Server) forward(method, dc string, args interface{}, reply interface{}) (bool, error) {
	// Handle DC forwarding
	if dc != s.config.Datacenter {
		err := s.forwardDC(method, dc, args, reply)
		return true, err
	}

	// Handle leader forwarding
	if !s.IsLeader() {
		err := s.forwardLeader(method, args, reply)
		return true, err
	}
	return false, nil
}

// forwardLeader is used to forward an RPC call to the leader, or fail if no leader
func (s *Server) forwardLeader(method string, args interface{}, reply interface{}) error {
	leader := s.raft.Leader()
	if leader == nil {
		return structs.ErrNoLeader
	}
	return s.connPool.RPC(leader, method, args, reply)
}

// forwardDC is used to forward an RPC call to a remote DC, or fail if no servers
func (s *Server) forwardDC(method, dc string, args interface{}, reply interface{}) error {
	// Bail if we can't find any servers
	s.remoteLock.RLock()
	servers := s.remoteConsuls[dc]
	if len(servers) == 0 {
		s.remoteLock.RUnlock()
		s.logger.Printf("[WARN] consul.rpc: RPC request for DC '%s', no path found", dc)
		return structs.ErrNoDCPath
	}

	// Select a random addr
	offset := rand.Int31() % int32(len(servers))
	server := servers[offset]
	s.remoteLock.RUnlock()

	// Forward to remote Consul
	metrics.IncrCounter([]string{"consul", "rpc", "cross-dc", dc}, 1)
	return s.connPool.RPC(server, method, args, reply)
}

// raftApply is used to encode a message, run it through raft, and return
// the FSM response along with any errors
func (s *Server) raftApply(t structs.MessageType, msg interface{}) (interface{}, error) {
	buf, err := structs.Encode(t, msg)
	if err != nil {
		return nil, fmt.Errorf("Failed to encode request: %v", err)
	}

	future := s.raft.Apply(buf, 0)
	if err := future.Error(); err != nil {
		return nil, err
	}

	return future.Response(), nil
}

// blockingRPC is used for queries that need to wait for a
// minimum index. This is used to block and wait for changes.
func (s *Server) blockingRPC(b *structs.BlockingQuery, tables MDBTables, run func() (uint64, error)) error {
	var timeout <-chan time.Time
	var notifyCh chan struct{}

	// Fast path non-blocking
	if b.MinQueryIndex == 0 {
		goto RUN_QUERY
	}

	// Sanity check that we have tables to block on
	if len(tables) == 0 {
		panic("no tables to block on")
	}

	// Restrict the max query time
	if b.MaxQueryTime > maxQueryTime {
		b.MaxQueryTime = maxQueryTime
	}

	// Ensure a time limit is set if we have an index
	if b.MinQueryIndex > 0 && b.MaxQueryTime == 0 {
		b.MaxQueryTime = maxQueryTime
	}

	// Setup a query timeout
	if b.MaxQueryTime > 0 {
		timeout = time.After(b.MaxQueryTime)
	}

	// Setup a notification channel for changes
SETUP_NOTIFY:
	if b.MinQueryIndex > 0 {
		notifyCh = make(chan struct{}, 1)
		s.fsm.State().Watch(tables, notifyCh)
	}

	// Run the query function
RUN_QUERY:
	idx, err := run()

	// Check for minimum query time
	if err == nil && idx <= b.MinQueryIndex {
		select {
		case <-notifyCh:
			goto SETUP_NOTIFY
		case <-timeout:
		}
	}
	return err
}
<|endoftext|>


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

package cpu

import "runtime"

const cacheLineSize = 64

func initOptions() {
	options = []option{
		{Name: "fp", Feature: &ARM64.HasFP},
		{Name: "asimd", Feature: &ARM64.HasASIMD},
		{Name: "evstrm", Feature: &ARM64.HasEVTSTRM},
		{Name: "aes", Feature: &ARM64.HasAES},
		{Name: "fphp", Feature: &ARM64.HasFPHP},
		{Name: "jscvt", Feature: &ARM64.HasJSCVT},
		{Name: "lrcpc", Feature: &ARM64.HasLRCPC},
		{Name: "pmull", Feature: &ARM64.HasPMULL},
		{Name: "sha1", Feature: &ARM64.HasSHA1},
		{Name: "sha2", Feature: &ARM64.HasSHA2},
		{Name: "sha3", Feature: &ARM64.HasSHA3},
		{Name: "sha512", Feature: &ARM64.HasSHA512},
		{Name: "sm3", Feature: &ARM64.HasSM3},
		{Name: "sm4", Feature: &ARM64.HasSM4},
		{Name: "sve", Feature: &ARM64.HasSVE},
		{Name: "crc32", Feature: &ARM64.HasCRC32},
		{Name: "atomics", Feature: &ARM64.HasATOMICS},
		{Name: "asimdhp", Feature: &ARM64.HasASIMDHP},
		{Name: "cpuid", Feature: &ARM64.HasCPUID},
		{Name: "asimrdm", Feature: &ARM64.HasASIMDRDM},
		{Name: "fcma", Feature: &ARM64.HasFCMA},
		{Name: "dcpop", Feature: &ARM64.HasDCPOP},
		{Name: "asimddp", Feature: &ARM64.HasASIMDDP},
		{Name: "asimdfhm", Feature: &ARM64.HasASIMDFHM},
	}
}

func archInit() {
	switch runtime.GOOS {
	case "android", "darwin", "ios", "netbsd":
		// Android and iOS don't seem to allow reading these registers.
		//
		// NetBSD:
		// ID_AA64ISAR0_EL1 is a privileged register and cannot be read from EL0.
		// It can be read via sysctl(3). Example for future implementers:
		// https://nxr.netbsd.org/xref/src/usr.sbin/cpuctl/arch/aarch64.c
		//
		// Fake the minimal features expected by
		// TestARM64minimalFeatures.
		ARM64.HasASIMD = true
		ARM64.HasFP = true
	case "linux":
		doinit()
	default:
		readARM64Registers()
	}
}

func readARM64Registers() {
	Initialized = true

	// ID_AA64ISAR0_EL1
	isar0 := getisar0()

	switch extractBits(isar0, 4, 7) {
	case 1:
		ARM64.HasAES = true
	case 2:
		ARM64.HasAES = true
		ARM64.HasPMULL = true
	}

	switch extractBits(isar0, 8, 11) {
	case 1:
		ARM64.HasSHA1 = true
	}

	switch extractBits(isar0, 12, 15) {
	case 1:
		ARM64.HasSHA2 = true
	case 2:
		ARM64.HasSHA2 = true
		ARM64.HasSHA512 = true
	}

	switch extractBits(isar0, 16, 19) {
	case 1:
		ARM64.HasCRC32 = true
	}

	switch extractBits(isar0, 20, 23) {
	case 2:
		ARM64.HasATOMICS = true
	}

	switch extractBits(isar0, 28, 31) {
	case 1:
		ARM64.HasASIMDRDM = true
	}

	switch extractBits(isar0, 32, 35) {
	case 1:
		ARM64.HasSHA3 = true
	}

	switch extractBits(isar0, 36, 39) {
	case 1:
		ARM64.HasSM3 = true
	}

	switch extractBits(isar0, 40, 43) {
	case 1:
		ARM64.HasSM4 = true
	}

	switch extractBits(isar0, 44, 47) {
	case 1:
		ARM64.HasASIMDDP = true
	}

	// ID_AA64ISAR1_EL1
	isar1 := getisar1()

	switch extractBits(isar1, 0, 3) {
	case 1:
		ARM64.HasDCPOP = true
	}

	switch extractBits(isar1, 12, 15) {
	case 1:
		ARM64.HasJSCVT = true
	}

	switch extractBits(isar1, 16, 19) {
	case 1:
		ARM64.HasFCMA = true
	}

	switch extractBits(isar1, 20, 23) {
	case 1:
		ARM64.HasLRCPC = true
	}

	// ID_AA64PFR0_EL1
	pfr0 := getpfr0()

	switch extractBits(pfr0, 16, 19) {
	case 0:
		ARM64.HasFP = true
	case 1:
		ARM64.HasFP = true
		ARM64.HasFPHP = true
	}

	switch extractBits(pfr0, 20, 23) {
	case 0:
		ARM64.HasASIMD = true
	case 1:
		ARM64.HasASIMD = true
		ARM64.HasASIMDHP = true
	}

	switch extractBits(pfr0, 32, 35) {
	case 1:
		ARM64.HasSVE = true
	}
}

func extractBits(data uint64, start, end uint) uint {
	return (uint)(data>>start) & ((1 << (end - start + 1)) - 1)
}
<commit_msg>cpu: avoid illegal instruction on openbsd/arm64<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cpu

import "runtime"

const cacheLineSize = 64

func initOptions() {
	options = []option{
		{Name: "fp", Feature: &ARM64.HasFP},
		{Name: "asimd", Feature: &ARM64.HasASIMD},
		{Name: "evstrm", Feature: &ARM64.HasEVTSTRM},
		{Name: "aes", Feature: &ARM64.HasAES},
		{Name: "fphp", Feature: &ARM64.HasFPHP},
		{Name: "jscvt", Feature: &ARM64.HasJSCVT},
		{Name: "lrcpc", Feature: &ARM64.HasLRCPC},
		{Name: "pmull", Feature: &ARM64.HasPMULL},
		{Name: "sha1", Feature: &ARM64.HasSHA1},
		{Name: "sha2", Feature: &ARM64.HasSHA2},
		{Name: "sha3", Feature: &ARM64.HasSHA3},
		{Name: "sha512", Feature: &ARM64.HasSHA512},
		{Name: "sm3", Feature: &ARM64.HasSM3},
		{Name: "sm4", Feature: &ARM64.HasSM4},
		{Name: "sve", Feature: &ARM64.HasSVE},
		{Name: "crc32", Feature: &ARM64.HasCRC32},
		{Name: "atomics", Feature: &ARM64.HasATOMICS},
		{Name: "asimdhp", Feature: &ARM64.HasASIMDHP},
		{Name: "cpuid", Feature: &ARM64.HasCPUID},
		{Name: "asimrdm", Feature: &ARM64.HasASIMDRDM},
		{Name: "fcma", Feature: &ARM64.HasFCMA},
		{Name: "dcpop", Feature: &ARM64.HasDCPOP},
		{Name: "asimddp", Feature: &ARM64.HasASIMDDP},
		{Name: "asimdfhm", Feature: &ARM64.HasASIMDFHM},
	}
}

func archInit() {
	switch runtime.GOOS {
	case "android", "darwin", "ios", "netbsd", "openbsd":
		// Android and iOS don't seem to allow reading these registers.
		//
		// NetBSD:
		// ID_AA64ISAR0_EL1 is a privileged register and cannot be read from EL0.
		// It can be read via sysctl(3). Example for future implementers:
		// https://nxr.netbsd.org/xref/src/usr.sbin/cpuctl/arch/aarch64.c
		//
		// OpenBSD:
		// See https://golang.org/issue/31746
		//
		// Fake the minimal features expected by
		// TestARM64minimalFeatures.
		ARM64.HasASIMD = true
		ARM64.HasFP = true
	case "linux":
		doinit()
	default:
		readARM64Registers()
	}
}

func readARM64Registers() {
	Initialized = true

	// ID_AA64ISAR0_EL1
	isar0 := getisar0()

	switch extractBits(isar0, 4, 7) {
	case 1:
		ARM64.HasAES = true
	case 2:
		ARM64.HasAES = true
		ARM64.HasPMULL = true
	}

	switch extractBits(isar0, 8, 11) {
	case 1:
		ARM64.HasSHA1 = true
	}

	switch extractBits(isar0, 12, 15) {
	case 1:
		ARM64.HasSHA2 = true
	case 2:
		ARM64.HasSHA2 = true
		ARM64.HasSHA512 = true
	}

	switch extractBits(isar0, 16, 19) {
	case 1:
		ARM64.HasCRC32 = true
	}

	switch extractBits(isar0, 20, 23) {
	case 2:
		ARM64.HasATOMICS = true
	}

	switch extractBits(isar0, 28, 31) {
	case 1:
		ARM64.HasASIMDRDM = true
	}

	switch extractBits(isar0, 32, 35) {
	case 1:
		ARM64.HasSHA3 = true
	}

	switch extractBits(isar0, 36, 39) {
	case 1:
		ARM64.HasSM3 = true
	}

	switch extractBits(isar0, 40, 43) {
	case 1:
		ARM64.HasSM4 = true
	}

	switch extractBits(isar0, 44, 47) {
	case 1:
		ARM64.HasASIMDDP = true
	}

	// ID_AA64ISAR1_EL1
	isar1 := getisar1()

	switch extractBits(isar1, 0, 3) {
	case 1:
		ARM64.HasDCPOP = true
	}

	switch extractBits(isar1, 12, 15) {
	case 1:
		ARM64.HasJSCVT = true
	}

	switch extractBits(isar1, 16, 19) {
	case 1:
		ARM64.HasFCMA = true
	}

	switch extractBits(isar1, 20, 23) {
	case 1:
		ARM64.HasLRCPC = true
	}

	// ID_AA64PFR0_EL1
	pfr0 := getpfr0()

	switch extractBits(pfr0, 16, 19) {
	case 0:
		ARM64.HasFP = true
	case 1:
		ARM64.HasFP = true
		ARM64.HasFPHP = true
	}

	switch extractBits(pfr0, 20, 23) {
	case 0:
		ARM64.HasASIMD = true
	case 1:
		ARM64.HasASIMD = true
		ARM64.HasASIMDHP = true
	}

	switch extractBits(pfr0, 32, 35) {
	case 1:
		ARM64.HasSVE = true
	}
}

func extractBits(data uint64, start, end uint) uint {
	return (uint)(data>>start) & ((1 << (end - start + 1)) - 1)
}
<|endoftext|>


<commit_before>// +build darwin linux

package collectors

import (
	"fmt"
	"io/ioutil"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/garyburd/redigo/redis"

	"github.com/oliveagle/go-collectors/datapoint"
	"github.com/oliveagle/go-collectors/metadata"
	"github.com/oliveagle/go-collectors/util"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: c_redis, init: redisInit})
}

var redisFields = map[string]bool{
	"aof_enabled":                  true,
	"aof_rewrite_in_progress":      true,
	"aof_rewrite_scheduled":        true,
	"aof_last_rewrite_time_sec":    true,
	"aof_current_rewrite_time_sec": true,
	"aof_last_bgrewrite_status":    true,
	"bgrewriteaof_in_progress":     true,
	"bgsave_in_progress":           true,
	"blocked_clients":              true,
	"changes_since_last_save":      true,
	"client_biggest_input_buf":     true,
	"client_longest_output_list":   true,
	"connected_clients":            true,
	"connected_slaves":             true,
	"evicted_keys":                 true,
	"expired_keys":                 true,
	"hash_max_zipmap_entries":      true,
	"hash_max_zipmap_value":        true,
	"keyspace_hits":                true,
	"keyspace_misses":              true,
	"master_link_status":           true,
	"master_sync_in_progress":      true,
	"master_last_io_seconds_ago":   true,
	"master_sync_left_bytes":       true,
	"mem_fragmentation_ratio":      true,
	"pubsub_channels":              true,
	"pubsub_patterns":              true,
	"rdb_changes_since_last_save":  true,
	"rdb_bgsave_in_progress":       true,
	"rdb_last_save_time":           true,
	"rdb_last_bgsave_status":       true,
	"rdb_last_bgsave_time_sec":     true,
	"rdb_current_bgsave_time_sec":  true,
	"role": true,
	"total_commands_processed":   true,
	"total_connections_received": true,
	"uptime_in_seconds":          true,
	"used_cpu_sys":               true,
	"used_cpu_user":              true,
	"used_memory":                true,
	"used_memory_rss":            true,
}

// For master_link_status.
var redisMlsMap = map[string]string{
	"up":   "1",
	"down": "0",
}

// For aof_last_bgrewrite_status, rdb_last_bgsave_status.
func status(s string) string {
	if s == "ok" {
		return "1"
	}
	return "0"
}

// For role which translates to is_slave
func slave(s string) string {
	if s == "slave" {
		return "1"
	}
	return "0"
}

var (
	tcRE           = regexp.MustCompile(`^\s*#\s*scollector.(\w+)\s*=\s*(.+)$`)
	redisInstances map[string]string
	redisLock      sync.Mutex
)

func redisInit() {
	update := func() {
		ri := make(map[string]string)
		oldRedis := false
		add := func(port, pid string) {
			cluster := fmt.Sprintf("port-%s", port)
			defer func() {
				ri[port] = cluster
			}()
			f, err := ioutil.ReadFile(fmt.Sprintf("/proc/%s/cmdline", pid))
			if err != nil {
				return
			}
			fsp := strings.Split(strings.Split(string(f), "\n")[0], "\u0000")
			if len(fsp) < 2 {
				return
			}
			cfg := fsp[len(fsp)-2]
			if len(cfg) == 0 {
				return
			}
			readLine(cfg, func(cfgline string) error {
				result := tcRE.FindStringSubmatch(cfgline)
				if len(result) > 2 && strings.ToLower(result[0]) == "cluster" {
					cluster = strings.ToLower(result[1])
				}
				return nil
			})
		}
		util.ReadCommand(func(line string) error {
			sp := strings.Fields(line)
			if len(sp) != 3 || !strings.HasSuffix(sp[1], "redis-server") {
				return nil
			}
			if !strings.Contains(sp[2], ":") {
				oldRedis = true
				return nil
			}
			pid := sp[0]
			port := strings.Split(sp[2], ":")[1]
			if port != "0" {
				add(port, pid)
			}
			return nil
		}, "ps", "-e", "-o", "pid,args")
		if oldRedis {
			util.ReadCommand(func(line string) error {
				if !strings.Contains(line, "redis-server") {
					return nil
				}
				sp := strings.Fields(line)
				if len(sp) < 7 || !strings.Contains(sp[3], ":") {
					return nil
				}
				pid := strings.Split(sp[6], "/")[0]
				port := strings.Split(sp[3], ":")[1]
				add(port, pid)
				return nil
			}, "netstat", "-tnlp")
		}
		redisLock.Lock()
		redisInstances = ri
		redisLock.Unlock()
	}
	update()
	go func() {
		for range time.Tick(time.Minute * 5) {
			update()
		}
	}()
}

func c_redis() (datapoint.MultiDataPoint, error) {
	var md datapoint.MultiDataPoint
	redisLock.Lock()
	var Error error
	for port, cluster := range redisInstances {
		c, err := redis.Dial("tcp", fmt.Sprintf(":%s", port))
		if err != nil {
			Error = err
			continue
		}
		defer c.Close()
		tags := datapoint.TagSet{
			"cluster": cluster,
			"port":    port,
		}
		lines, err := c.Do("INFO")
		if err != nil {
			Error = err
			continue
		}
		_ = tags
		for _, line := range strings.Split(string(lines.([]uint8)), "\n") {
			line = strings.TrimSpace(line)
			sp := strings.Split(line, ":")
			if len(sp) < 2 || !redisFields[sp[0]] {
				continue
			}
			if sp[0] == "master_link_status" {
				Add(&md, "redis."+sp[0], redisMlsMap[sp[1]], tags, metadata.Unknown, metadata.None, "")
				continue
			}
			if sp[0] == "role" {
				Add(&md, "redis.is_slave", slave(sp[1]), tags, metadata.Gauge, metadata.Bool, "")
				continue
			}
			if sp[0] == "aof_last_bgrewrite_status" || sp[0] == "rdb_last_bgsave_status" {
				Add(&md, "redis."+sp[0], status(sp[1]), tags, metadata.Unknown, metadata.None, "")
				continue
			}
			Add(&md, "redis."+sp[0], sp[1], tags, metadata.Unknown, metadata.None, "")
		}
	}
	redisLock.Unlock()
	return md, Error
}
<commit_msg>fix for range bug<commit_after>// +build darwin linux

package collectors

import (
	"fmt"
	"io/ioutil"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/garyburd/redigo/redis"

	"github.com/oliveagle/go-collectors/datapoint"
	"github.com/oliveagle/go-collectors/metadata"
	"github.com/oliveagle/go-collectors/util"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: c_redis, init: redisInit})
}

var redisFields = map[string]bool{
	"aof_enabled":                  true,
	"aof_rewrite_in_progress":      true,
	"aof_rewrite_scheduled":        true,
	"aof_last_rewrite_time_sec":    true,
	"aof_current_rewrite_time_sec": true,
	"aof_last_bgrewrite_status":    true,
	"bgrewriteaof_in_progress":     true,
	"bgsave_in_progress":           true,
	"blocked_clients":              true,
	"changes_since_last_save":      true,
	"client_biggest_input_buf":     true,
	"client_longest_output_list":   true,
	"connected_clients":            true,
	"connected_slaves":             true,
	"evicted_keys":                 true,
	"expired_keys":                 true,
	"hash_max_zipmap_entries":      true,
	"hash_max_zipmap_value":        true,
	"keyspace_hits":                true,
	"keyspace_misses":              true,
	"master_link_status":           true,
	"master_sync_in_progress":      true,
	"master_last_io_seconds_ago":   true,
	"master_sync_left_bytes":       true,
	"mem_fragmentation_ratio":      true,
	"pubsub_channels":              true,
	"pubsub_patterns":              true,
	"rdb_changes_since_last_save":  true,
	"rdb_bgsave_in_progress":       true,
	"rdb_last_save_time":           true,
	"rdb_last_bgsave_status":       true,
	"rdb_last_bgsave_time_sec":     true,
	"rdb_current_bgsave_time_sec":  true,
	"role": true,
	"total_commands_processed":   true,
	"total_connections_received": true,
	"uptime_in_seconds":          true,
	"used_cpu_sys":               true,
	"used_cpu_user":              true,
	"used_memory":                true,
	"used_memory_rss":            true,
}

// For master_link_status.
var redisMlsMap = map[string]string{
	"up":   "1",
	"down": "0",
}

// For aof_last_bgrewrite_status, rdb_last_bgsave_status.
func status(s string) string {
	if s == "ok" {
		return "1"
	}
	return "0"
}

// For role which translates to is_slave
func slave(s string) string {
	if s == "slave" {
		return "1"
	}
	return "0"
}

var (
	tcRE           = regexp.MustCompile(`^\s*#\s*scollector.(\w+)\s*=\s*(.+)$`)
	redisInstances map[string]string
	redisLock      sync.Mutex
)

func redisInit() {
	update := func() {
		ri := make(map[string]string)
		oldRedis := false
		add := func(port, pid string) {
			cluster := fmt.Sprintf("port-%s", port)
			defer func() {
				ri[port] = cluster
			}()
			f, err := ioutil.ReadFile(fmt.Sprintf("/proc/%s/cmdline", pid))
			if err != nil {
				return
			}
			fsp := strings.Split(strings.Split(string(f), "\n")[0], "\u0000")
			if len(fsp) < 2 {
				return
			}
			cfg := fsp[len(fsp)-2]
			if len(cfg) == 0 {
				return
			}
			readLine(cfg, func(cfgline string) error {
				result := tcRE.FindStringSubmatch(cfgline)
				if len(result) > 2 && strings.ToLower(result[0]) == "cluster" {
					cluster = strings.ToLower(result[1])
				}
				return nil
			})
		}
		util.ReadCommand(func(line string) error {
			sp := strings.Fields(line)
			if len(sp) != 3 || !strings.HasSuffix(sp[1], "redis-server") {
				return nil
			}
			if !strings.Contains(sp[2], ":") {
				oldRedis = true
				return nil
			}
			pid := sp[0]
			port := strings.Split(sp[2], ":")[1]
			if port != "0" {
				add(port, pid)
			}
			return nil
		}, "ps", "-e", "-o", "pid,args")
		if oldRedis {
			util.ReadCommand(func(line string) error {
				if !strings.Contains(line, "redis-server") {
					return nil
				}
				sp := strings.Fields(line)
				if len(sp) < 7 || !strings.Contains(sp[3], ":") {
					return nil
				}
				pid := strings.Split(sp[6], "/")[0]
				port := strings.Split(sp[3], ":")[1]
				add(port, pid)
				return nil
			}, "netstat", "-tnlp")
		}
		redisLock.Lock()
		redisInstances = ri
		redisLock.Unlock()
	}
	update()
	go func() {
		for _ = range time.Tick(time.Minute * 5) {
			update()
		}
	}()
}

func c_redis() (datapoint.MultiDataPoint, error) {
	var md datapoint.MultiDataPoint
	redisLock.Lock()
	var Error error
	for port, cluster := range redisInstances {
		c, err := redis.Dial("tcp", fmt.Sprintf(":%s", port))
		if err != nil {
			Error = err
			continue
		}
		defer c.Close()
		tags := datapoint.TagSet{
			"cluster": cluster,
			"port":    port,
		}
		lines, err := c.Do("INFO")
		if err != nil {
			Error = err
			continue
		}
		_ = tags
		for _, line := range strings.Split(string(lines.([]uint8)), "\n") {
			line = strings.TrimSpace(line)
			sp := strings.Split(line, ":")
			if len(sp) < 2 || !redisFields[sp[0]] {
				continue
			}
			if sp[0] == "master_link_status" {
				Add(&md, "redis."+sp[0], redisMlsMap[sp[1]], tags, metadata.Unknown, metadata.None, "")
				continue
			}
			if sp[0] == "role" {
				Add(&md, "redis.is_slave", slave(sp[1]), tags, metadata.Gauge, metadata.Bool, "")
				continue
			}
			if sp[0] == "aof_last_bgrewrite_status" || sp[0] == "rdb_last_bgsave_status" {
				Add(&md, "redis."+sp[0], status(sp[1]), tags, metadata.Unknown, metadata.None, "")
				continue
			}
			Add(&md, "redis."+sp[0], sp[1], tags, metadata.Unknown, metadata.None, "")
		}
	}
	redisLock.Unlock()
	return md, Error
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"fmt"
	"time"

	"github.com/jinzhu/gorm"
)

//Campaign is a struct representing a created campaign
type Campaign struct {
	Id            int64     `json:"id"`
	UserId        int64     `json:"-"`
	Name          string    `json:"name" sql:"not null"`
	CreatedDate   time.Time `json:"created_date"`
	CompletedDate time.Time `json:"completed_date"`
	TemplateId    int64     `json:"-"`
	Template      Template  `json:"template"`
	PageId        int64     `json:"-"`
	Page          Page      `json:"page"`
	Status        string    `json:"status"`
	Results       []Result  `json:"results,omitempty"`
	Groups        []Group   `json:"groups,omitempty"`
	Events        []Event   `json:"timeline,omitemtpy"`
	SMTPId        int64     `json:"-"`
	SMTP          SMTP      `json:"smtp"`
	URL           string    `json:"url"`
}

// ErrCampaignNameNotSpecified indicates there was no template given by the user
var ErrCampaignNameNotSpecified = errors.New("Campaign name not specified")

// ErrGroupNotSpecified indicates there was no template given by the user
var ErrGroupNotSpecified = errors.New("No groups specified")

// ErrTemplateNotSpecified indicates there was no template given by the user
var ErrTemplateNotSpecified = errors.New("No email template specified")

// ErrPageNotSpecified indicates a landing page was not provided for the campaign
var ErrPageNotSpecified = errors.New("No landing page specified")

// ErrSMTPNotSpecified indicates a sending profile was not provided for the campaign
var ErrSMTPNotSpecified = errors.New("No sending profile specified")

// ErrTemplateNotFound indicates the template specified does not exist in the database
var ErrTemplateNotFound = errors.New("Template not found")

// ErrGroupnNotFound indicates a group specified by the user does not exist in the database
var ErrGroupNotFound = errors.New("Group not found")

// ErrPageNotFound indicates a page specified by the user does not exist in the database
var ErrPageNotFound = errors.New("Page not found")

// ErrSMTPNotFound indicates a sending profile specified by the user does not exist in the database
var ErrSMTPNotFound = errors.New("Sending profile not found")

// Validate checks to make sure there are no invalid fields in a submitted campaign
func (c *Campaign) Validate() error {
	switch {
	case c.Name == "":
		return ErrCampaignNameNotSpecified
	case len(c.Groups) == 0:
		return ErrGroupNotSpecified
	case c.Template.Name == "":
		return ErrTemplateNotSpecified
	case c.Page.Name == "":
		return ErrPageNotSpecified
	case c.SMTP.Name == "":
		return ErrSMTPNotSpecified
	}
	return nil
}

// SendTestEmailRequest is the structure of a request
// to send a test email to test an SMTP connection
type SendTestEmailRequest struct {
	Template    Template `json:"template"`
	Page        Page     `json:"page"`
	SMTP        SMTP     `json:"smtp"`
	URL         string   `json:"url"`
	Tracker     string   `json:"tracker"`
	TrackingURL string   `json:"tracking_url"`
	From        string   `json:"from"`
	Target
}

// Validate ensures the SendTestEmailRequest structure
// is valid.
func (s *SendTestEmailRequest) Validate() error {
	switch {
	case s.Email == "":
		return ErrEmailNotSpecified
	}
	return nil
}

// UpdateStatus changes the campaign status appropriately
func (c *Campaign) UpdateStatus(s string) error {
	// This could be made simpler, but I think there's a bug in gorm
	return db.Table("campaigns").Where("id=?", c.Id).Update("status", s).Error
}

// AddEvent creates a new campaign event in the database
func (c *Campaign) AddEvent(e Event) error {
	e.CampaignId = c.Id
	e.Time = time.Now()
	return db.Debug().Save(&e).Error
}

// Event contains the fields for an event
// that occurs during the campaign
type Event struct {
	Id         int64     `json:"-"`
	CampaignId int64     `json:"-"`
	Email      string    `json:"email"`
	Time       time.Time `json:"time"`
	Message    string    `json:"message"`
	Details    string    `json:"details"`
}

// GetCampaigns returns the campaigns owned by the given user.
func GetCampaigns(uid int64) ([]Campaign, error) {
	cs := []Campaign{}
	err := db.Model(&User{Id: uid}).Related(&cs).Error
	if err != nil {
		fmt.Println(err)
	}
	for i, _ := range cs {
		err := db.Model(&cs[i]).Related(&cs[i].Results).Error
		if err != nil {
			Logger.Println(err)
		}
		err = db.Model(&cs[i]).Related(&cs[i].Events).Error
		if err != nil {
			Logger.Println(err)
		}
		err = db.Table("templates").Where("id=?", cs[i].TemplateId).Find(&cs[i].Template).Error
		if err != nil {
			Logger.Println(err)
		}
		err = db.Table("pages").Where("id=?", cs[i].PageId).Find(&cs[i].Page).Error
		if err != nil {
			Logger.Println(err)
		}
		err = db.Table("SMTP").Where("id=?", cs[i].SMTPId).Find(&cs[i].SMTP).Error
                if err != nil {
                        Logger.Println(err)
                }
	}
	return cs, err
}

// GetCampaign returns the campaign, if it exists, specified by the given id and user_id.
func GetCampaign(id int64, uid int64) (Campaign, error) {
	c := Campaign{}
	err := db.Where("id = ?", id).Where("user_id = ?", uid).Find(&c).Error
	if err != nil {
		Logger.Printf("%s: campaign not found\n", err)
		return c, err
	}
	err = db.Model(&c).Related(&c.Results).Error
	if err != nil {
		Logger.Printf("%s: results not found for campaign\n", err)
		return c, err
	}
	err = db.Model(&c).Related(&c.Events).Error
	if err != nil {
		Logger.Printf("%s: events not found for campaign\n", err)
		return c, err
	}
	err = db.Table("templates").Where("id=?", c.TemplateId).Find(&c.Template).Error
	if err != nil {
		Logger.Printf("%s: template not found for campaign\n", err)
		return c, err
	}
	err = db.Table("pages").Where("id=?", c.PageId).Find(&c.Page).Error
	if err != nil {
		Logger.Printf("%s: page not found for campaign\n", err)
	}
	err = db.Table("SMTP").Where("id=?", c.SMTPId).Find(&c.SMTP).Error
	if err != nil {
		Logger.Printf("%s: sending profile not found for campaign\n", err)
	}
	return c, err
}

// PostCampaign inserts a campaign and all associated records into the database.
func PostCampaign(c *Campaign, uid int64) error {
	if err := c.Validate(); err != nil {
		return err
	}
	// Fill in the details
	c.UserId = uid
	c.CreatedDate = time.Now()
	c.CompletedDate = time.Time{}
	c.Status = CAMPAIGN_QUEUED
	// Check to make sure all the groups already exist
	for i, g := range c.Groups {
		c.Groups[i], err = GetGroupByName(g.Name, uid)
		if err == gorm.RecordNotFound {
			Logger.Printf("Error - Group %s does not exist", g.Name)
			return ErrGroupNotFound
		} else if err != nil {
			Logger.Println(err)
			return err
		}
	}
	// Check to make sure the template exists
	t, err := GetTemplateByName(c.Template.Name, uid)
	if err == gorm.RecordNotFound {
		Logger.Printf("Error - Template %s does not exist", t.Name)
		return ErrTemplateNotFound
	} else if err != nil {
		Logger.Println(err)
		return err
	}
	c.Template = t
	c.TemplateId = t.Id
	// Check to make sure the page exists
	p, err := GetPageByName(c.Page.Name, uid)
	if err == gorm.RecordNotFound {
		Logger.Printf("Error - Page %s does not exist", p.Name)
		return ErrPageNotFound
	} else if err != nil {
		Logger.Println(err)
		return err
	}
	c.Page = p
	c.PageId = p.Id
	// Check to make sure the sending profile exists
	s, err := GetSMTPByName(c.SMTP.Name, uid)
	if err == gorm.RecordNotFound {
		Logger.Printf("Error - Sending profile %s does not exist", s.Name)
		return ErrPageNotFound
	} else if err != nil {
		Logger.Println(err)
		return err
	}
	c.SMTP = s
	c.SMTPId = s.Id
	// Insert into the DB
	err = db.Save(c).Error
	if err != nil {
		Logger.Println(err)
		return err
	}
	err = c.AddEvent(Event{Message: "Campaign Created"})
	if err != nil {
		Logger.Println(err)
	}
	// Insert all the results
	for _, g := range c.Groups {
		// Insert a result for each target in the group
		for _, t := range g.Targets {
			r := &Result{Email: t.Email, Position: t.Position, Status: STATUS_SENDING, CampaignId: c.Id, UserId: c.UserId, FirstName: t.FirstName, LastName: t.LastName}
			r.GenerateId()
			err = db.Save(r).Error
			if err != nil {
				Logger.Printf("Error adding result record for target %s\n", t.Email)
				Logger.Println(err)
			}
			c.Results = append(c.Results, *r)
		}
	}
	return nil
}

//DeleteCampaign deletes the specified campaign
func DeleteCampaign(id int64) error {
	Logger.Printf("Deleting campaign %d\n", id)
	// Delete all the campaign results
	err := db.Where("campaign_id=?", id).Delete(&Result{}).Error
	if err != nil {
		Logger.Println(err)
		return err
	}
	err = db.Where("campaign_id=?", id).Delete(&Event{}).Error
	if err != nil {
		Logger.Println(err)
		return err
	}
	// Delete the campaign
	err = db.Delete(&Campaign{Id: id}).Error
	if err != nil {
		Logger.Panicln(err)
		return err
	}
	return err
}
<commit_msg>Ignoring error for no sending profile found. Fixes #189<commit_after>package models

import (
	"errors"
	"fmt"
	"time"

	"github.com/jinzhu/gorm"
)

//Campaign is a struct representing a created campaign
type Campaign struct {
	Id            int64     `json:"id"`
	UserId        int64     `json:"-"`
	Name          string    `json:"name" sql:"not null"`
	CreatedDate   time.Time `json:"created_date"`
	CompletedDate time.Time `json:"completed_date"`
	TemplateId    int64     `json:"-"`
	Template      Template  `json:"template"`
	PageId        int64     `json:"-"`
	Page          Page      `json:"page"`
	Status        string    `json:"status"`
	Results       []Result  `json:"results,omitempty"`
	Groups        []Group   `json:"groups,omitempty"`
	Events        []Event   `json:"timeline,omitemtpy"`
	SMTPId        int64     `json:"-"`
	SMTP          SMTP      `json:"smtp"`
	URL           string    `json:"url"`
}

// ErrCampaignNameNotSpecified indicates there was no template given by the user
var ErrCampaignNameNotSpecified = errors.New("Campaign name not specified")

// ErrGroupNotSpecified indicates there was no template given by the user
var ErrGroupNotSpecified = errors.New("No groups specified")

// ErrTemplateNotSpecified indicates there was no template given by the user
var ErrTemplateNotSpecified = errors.New("No email template specified")

// ErrPageNotSpecified indicates a landing page was not provided for the campaign
var ErrPageNotSpecified = errors.New("No landing page specified")

// ErrSMTPNotSpecified indicates a sending profile was not provided for the campaign
var ErrSMTPNotSpecified = errors.New("No sending profile specified")

// ErrTemplateNotFound indicates the template specified does not exist in the database
var ErrTemplateNotFound = errors.New("Template not found")

// ErrGroupnNotFound indicates a group specified by the user does not exist in the database
var ErrGroupNotFound = errors.New("Group not found")

// ErrPageNotFound indicates a page specified by the user does not exist in the database
var ErrPageNotFound = errors.New("Page not found")

// ErrSMTPNotFound indicates a sending profile specified by the user does not exist in the database
var ErrSMTPNotFound = errors.New("Sending profile not found")

// Validate checks to make sure there are no invalid fields in a submitted campaign
func (c *Campaign) Validate() error {
	switch {
	case c.Name == "":
		return ErrCampaignNameNotSpecified
	case len(c.Groups) == 0:
		return ErrGroupNotSpecified
	case c.Template.Name == "":
		return ErrTemplateNotSpecified
	case c.Page.Name == "":
		return ErrPageNotSpecified
	case c.SMTP.Name == "":
		return ErrSMTPNotSpecified
	}
	return nil
}

// SendTestEmailRequest is the structure of a request
// to send a test email to test an SMTP connection
type SendTestEmailRequest struct {
	Template    Template `json:"template"`
	Page        Page     `json:"page"`
	SMTP        SMTP     `json:"smtp"`
	URL         string   `json:"url"`
	Tracker     string   `json:"tracker"`
	TrackingURL string   `json:"tracking_url"`
	From        string   `json:"from"`
	Target
}

// Validate ensures the SendTestEmailRequest structure
// is valid.
func (s *SendTestEmailRequest) Validate() error {
	switch {
	case s.Email == "":
		return ErrEmailNotSpecified
	}
	return nil
}

// UpdateStatus changes the campaign status appropriately
func (c *Campaign) UpdateStatus(s string) error {
	// This could be made simpler, but I think there's a bug in gorm
	return db.Table("campaigns").Where("id=?", c.Id).Update("status", s).Error
}

// AddEvent creates a new campaign event in the database
func (c *Campaign) AddEvent(e Event) error {
	e.CampaignId = c.Id
	e.Time = time.Now()
	return db.Debug().Save(&e).Error
}

// Event contains the fields for an event
// that occurs during the campaign
type Event struct {
	Id         int64     `json:"-"`
	CampaignId int64     `json:"-"`
	Email      string    `json:"email"`
	Time       time.Time `json:"time"`
	Message    string    `json:"message"`
	Details    string    `json:"details"`
}

// GetCampaigns returns the campaigns owned by the given user.
func GetCampaigns(uid int64) ([]Campaign, error) {
	cs := []Campaign{}
	err := db.Model(&User{Id: uid}).Related(&cs).Error
	if err != nil {
		fmt.Println(err)
	}
	for i, _ := range cs {
		err := db.Model(&cs[i]).Related(&cs[i].Results).Error
		if err != nil {
			Logger.Println(err)
		}
		err = db.Model(&cs[i]).Related(&cs[i].Events).Error
		if err != nil {
			Logger.Println(err)
		}
		err = db.Table("templates").Where("id=?", cs[i].TemplateId).Find(&cs[i].Template).Error
		if err != nil {
			Logger.Println(err)
		}
		err = db.Table("pages").Where("id=?", cs[i].PageId).Find(&cs[i].Page).Error
		if err != nil {
			Logger.Println(err)
		}
		err = db.Table("SMTP").Where("id=?", cs[i].SMTPId).Find(&cs[i].SMTP).Error
		if err != nil {
			Logger.Println(err)
		}
	}
	return cs, err
}

// GetCampaign returns the campaign, if it exists, specified by the given id and user_id.
func GetCampaign(id int64, uid int64) (Campaign, error) {
	c := Campaign{}
	err := db.Where("id = ?", id).Where("user_id = ?", uid).Find(&c).Error
	if err != nil {
		Logger.Printf("%s: campaign not found\n", err)
		return c, err
	}
	err = db.Model(&c).Related(&c.Results).Error
	if err != nil {
		Logger.Printf("%s: results not found for campaign\n", err)
		return c, err
	}
	err = db.Model(&c).Related(&c.Events).Error
	if err != nil {
		Logger.Printf("%s: events not found for campaign\n", err)
		return c, err
	}
	err = db.Table("templates").Where("id=?", c.TemplateId).Find(&c.Template).Error
	if err != nil {
		Logger.Printf("%s: template not found for campaign\n", err)
		return c, err
	}
	err = db.Table("pages").Where("id=?", c.PageId).Find(&c.Page).Error
	if err != nil {
		Logger.Printf("%s: page not found for campaign\n", err)
	}
	err = db.Table("SMTP").Where("id=?", c.SMTPId).Find(&c.SMTP).Error
	if err != nil {
		// For now, since we just introduced the new sending profiles
		// we'll return nil for the error
		Logger.Printf("%s: sending profile not found for campaign\n", err)
		err = nil
	}
	return c, err
}

// PostCampaign inserts a campaign and all associated records into the database.
func PostCampaign(c *Campaign, uid int64) error {
	if err := c.Validate(); err != nil {
		return err
	}
	// Fill in the details
	c.UserId = uid
	c.CreatedDate = time.Now()
	c.CompletedDate = time.Time{}
	c.Status = CAMPAIGN_QUEUED
	// Check to make sure all the groups already exist
	for i, g := range c.Groups {
		c.Groups[i], err = GetGroupByName(g.Name, uid)
		if err == gorm.RecordNotFound {
			Logger.Printf("Error - Group %s does not exist", g.Name)
			return ErrGroupNotFound
		} else if err != nil {
			Logger.Println(err)
			return err
		}
	}
	// Check to make sure the template exists
	t, err := GetTemplateByName(c.Template.Name, uid)
	if err == gorm.RecordNotFound {
		Logger.Printf("Error - Template %s does not exist", t.Name)
		return ErrTemplateNotFound
	} else if err != nil {
		Logger.Println(err)
		return err
	}
	c.Template = t
	c.TemplateId = t.Id
	// Check to make sure the page exists
	p, err := GetPageByName(c.Page.Name, uid)
	if err == gorm.RecordNotFound {
		Logger.Printf("Error - Page %s does not exist", p.Name)
		return ErrPageNotFound
	} else if err != nil {
		Logger.Println(err)
		return err
	}
	c.Page = p
	c.PageId = p.Id
	// Check to make sure the sending profile exists
	s, err := GetSMTPByName(c.SMTP.Name, uid)
	if err == gorm.RecordNotFound {
		Logger.Printf("Error - Sending profile %s does not exist", s.Name)
		return ErrPageNotFound
	} else if err != nil {
		Logger.Println(err)
		return err
	}
	c.SMTP = s
	c.SMTPId = s.Id
	// Insert into the DB
	err = db.Save(c).Error
	if err != nil {
		Logger.Println(err)
		return err
	}
	err = c.AddEvent(Event{Message: "Campaign Created"})
	if err != nil {
		Logger.Println(err)
	}
	// Insert all the results
	for _, g := range c.Groups {
		// Insert a result for each target in the group
		for _, t := range g.Targets {
			r := &Result{Email: t.Email, Position: t.Position, Status: STATUS_SENDING, CampaignId: c.Id, UserId: c.UserId, FirstName: t.FirstName, LastName: t.LastName}
			r.GenerateId()
			err = db.Save(r).Error
			if err != nil {
				Logger.Printf("Error adding result record for target %s\n", t.Email)
				Logger.Println(err)
			}
			c.Results = append(c.Results, *r)
		}
	}
	return nil
}

//DeleteCampaign deletes the specified campaign
func DeleteCampaign(id int64) error {
	Logger.Printf("Deleting campaign %d\n", id)
	// Delete all the campaign results
	err := db.Where("campaign_id=?", id).Delete(&Result{}).Error
	if err != nil {
		Logger.Println(err)
		return err
	}
	err = db.Where("campaign_id=?", id).Delete(&Event{}).Error
	if err != nil {
		Logger.Println(err)
		return err
	}
	// Delete the campaign
	err = db.Delete(&Campaign{Id: id}).Error
	if err != nil {
		Logger.Panicln(err)
		return err
	}
	return err
}
<|endoftext|>