<commit_before>package archive

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"strings"

	"compress/bzip2"
	"compress/gzip"
	"xi2.org/x/xz"

	"golang.org/x/crypto/openpgp"
	"pault.ag/go/debian/control"
	"pault.ag/go/debian/dependency"
)

type PathReader func(path string) (io.Reader, Closer, error)

// Archive {{{

type Archive struct {
	root       string
	pathReader PathReader
	keyring    *openpgp.EntityList
}

// Get an archive Suite for a given Archive {{{

func (a Archive) Suite(name string) (*Suite, error) {
	inReleasePath := path.Join("dists", name, "InRelease")
	reader, closer, err := a.getFile(inReleasePath, nil)
	if err != nil {
		return nil, err
	}
	defer closer()

	release, err := LoadInRelease(reader, a.keyring)
	if err != nil {
		return nil, err
	}
	return &Suite{
		Release: *release,
		archive: a,
	}, nil
}

// }}}

// Suite {{{

// Suite struct {{{

type Suite struct {
	Release Release
	archive Archive
}

// }}}

// HasArch {{{

func (s Suite) HasArch(arch dependency.Arch) bool {
	for _, el := range s.Release.Architectures {
		if el.Is(&arch) {
			return true
		}
	}
	return false
}

// }}}

// HasComponent {{{

func (s Suite) HasComponent(component string) bool {
	for _, el := range s.Release.Components {
		if el == component {
			return true
		}
	}
	return false
}

// }}}

// getHashedFileReader - get a Hashed File {{{

func (s Suite) getHashedFileReader(suitePath string) (io.Reader, Closer, control.FileHashValidators, error) {
	hashes := s.Release.Indices()[suitePath]
	if len(hashes) == 0 {
		return nil, nil, nil, fmt.Errorf("Undeclared file in InRelease")
	}

	validators, err := hashes.Validators()
	if err != nil {
		return nil, nil, nil, err
	}

	packagesPath := path.Join("dists", s.Release.Suite, suitePath)
	reader, closer, err := s.archive.getFile(packagesPath, validators.Writer())

	if err != nil {
		return nil, nil, nil, err
	}

	return reader, closer, validators, err
}

// }}}

// Packages index for a Suite {{{

func (s Suite) Packages(component string, arch dependency.Arch) ([]Package, error) {
	ret := []Package{}

	if !s.HasComponent(component) {
		return ret, fmt.Errorf("No such component: '%s'", component)
	}
	if !s.HasArch(arch) {
		return ret, fmt.Errorf("No such arch: '%s'", arch.String())
	}
	suitePath := path.Join(
		component,
		fmt.Sprintf("binary-%s", arch.String()),
		"Packages",
	)

	validationReader, closer, validators, err := s.getHashedFileReader(suitePath)
	if err != nil {
		return []Package{}, err
	}
	defer closer()

	packages, err := LoadPackages(validationReader)
	if err != nil {
		return []Package{}, err
	}

	for {
		next, err := packages.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return []Package{}, err
		}
		ret = append(ret, *next)
	}

	if !validators.Validate() {
		return nil, fmt.Errorf("Index hashes don't match!")
	}

	return ret, nil
}

// }}}

// Sources index for a Suite {{{

func (s Suite) Sources(component string) ([]Source, error) {
	ret := []Source{}

	if !s.HasComponent(component) {
		return ret, fmt.Errorf("No such component: '%s'", component)
	}
	suitePath := path.Join(component, "source", "Sources.gz")

	validationReader, closer, validators, err := s.getHashedFileReader(suitePath)
	if err != nil {
		return []Source{}, err
	}
	defer closer()

	packages, err := LoadSources(validationReader)
	if err != nil {
		return []Source{}, err
	}

	for {
		next, err := packages.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return []Source{}, err
		}
		ret = append(ret, *next)
	}

	if !validators.Validate() {
		return nil, fmt.Errorf("Index hashes don't match!")
	}

	return ret, nil
}

// }}}

// }}}

// getFile wrapper {{{

func (a Archive) getFile(requestPath string, tee io.Writer) (io.Reader, Closer, error) {
	archivePath := a.root + "/" + requestPath
	reader, closer, err := a.pathReader(archivePath)
	if err != nil {
		return nil, nil, err
	}

	reader, err := compression.Decompress(reader, archivePath, tee)
	if err != nil {
		closer()
	}
	return reader, err

}

// }}}

// Constructors {{{

func NewArchive(
	root string,
	pathReader PathReader,
	keyring *openpgp.EntityList,
) Archive {
	return Archive{
		root:       root,
		pathReader: pathReader,
		keyring:    keyring,
	}
}

// Filesystem Archive {{{

func NewFilesystemArchive(root string, keyring *openpgp.EntityList) Archive {
	return NewArchive(root, filesystemPathReader, keyring)
}

func NewHttpArchive(root string, keyring *openpgp.EntityList) Archive {
	return NewArchive(root, httpPathReader, keyring)
}

// }}}

// }}}

// Readers {{{

func filesystemPathReader(path string) (io.Reader, Closer, error) {
	fd, err := os.Open(path)
	return fd, fd.Close, err
}

func httpPathReader(uri string) (io.Reader, Closer, error) {
	resp, err := http.Get(uri)
	if err != nil {
		return nil, nil, err
	}
	return resp.Body, resp.Body.Close, nil
}

// }}}

// }}}

// vim: foldmethod=marker
<commit_msg>remove things<commit_after>package archive

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"strings"

	"golang.org/x/crypto/openpgp"
	"pault.ag/go/debian/control"
	"pault.ag/go/debian/dependency"
)

type PathReader func(path string) (io.Reader, Closer, error)

// Archive {{{

type Archive struct {
	root       string
	pathReader PathReader
	keyring    *openpgp.EntityList
}

// Get an archive Suite for a given Archive {{{

func (a Archive) Suite(name string) (*Suite, error) {
	inReleasePath := path.Join("dists", name, "InRelease")
	reader, closer, err := a.getFile(inReleasePath, nil)
	if err != nil {
		return nil, err
	}
	defer closer()

	release, err := LoadInRelease(reader, a.keyring)
	if err != nil {
		return nil, err
	}
	return &Suite{
		Release: *release,
		archive: a,
	}, nil
}

// }}}

// Suite {{{

// Suite struct {{{

type Suite struct {
	Release Release
	archive Archive
}

// }}}

// HasArch {{{

func (s Suite) HasArch(arch dependency.Arch) bool {
	for _, el := range s.Release.Architectures {
		if el.Is(&arch) {
			return true
		}
	}
	return false
}

// }}}

// HasComponent {{{

func (s Suite) HasComponent(component string) bool {
	for _, el := range s.Release.Components {
		if el == component {
			return true
		}
	}
	return false
}

// }}}

// getHashedFileReader - get a Hashed File {{{

func (s Suite) getHashedFileReader(suitePath string) (io.Reader, Closer, control.FileHashValidators, error) {
	hashes := s.Release.Indices()[suitePath]
	if len(hashes) == 0 {
		return nil, nil, nil, fmt.Errorf("Undeclared file in InRelease")
	}

	validators, err := hashes.Validators()
	if err != nil {
		return nil, nil, nil, err
	}

	packagesPath := path.Join("dists", s.Release.Suite, suitePath)
	reader, closer, err := s.archive.getFile(packagesPath, validators.Writer())

	if err != nil {
		return nil, nil, nil, err
	}

	return reader, closer, validators, err
}

// }}}

// Packages index for a Suite {{{

func (s Suite) Packages(component string, arch dependency.Arch) ([]Package, error) {
	ret := []Package{}

	if !s.HasComponent(component) {
		return ret, fmt.Errorf("No such component: '%s'", component)
	}
	if !s.HasArch(arch) {
		return ret, fmt.Errorf("No such arch: '%s'", arch.String())
	}
	suitePath := path.Join(
		component,
		fmt.Sprintf("binary-%s", arch.String()),
		"Packages",
	)

	validationReader, closer, validators, err := s.getHashedFileReader(suitePath)
	if err != nil {
		return []Package{}, err
	}
	defer closer()

	packages, err := LoadPackages(validationReader)
	if err != nil {
		return []Package{}, err
	}

	for {
		next, err := packages.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return []Package{}, err
		}
		ret = append(ret, *next)
	}

	if !validators.Validate() {
		return nil, fmt.Errorf("Index hashes don't match!")
	}

	return ret, nil
}

// }}}

// Sources index for a Suite {{{

func (s Suite) Sources(component string) ([]Source, error) {
	ret := []Source{}

	if !s.HasComponent(component) {
		return ret, fmt.Errorf("No such component: '%s'", component)
	}
	suitePath := path.Join(component, "source", "Sources.gz")

	validationReader, closer, validators, err := s.getHashedFileReader(suitePath)
	if err != nil {
		return []Source{}, err
	}
	defer closer()

	packages, err := LoadSources(validationReader)
	if err != nil {
		return []Source{}, err
	}

	for {
		next, err := packages.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return []Source{}, err
		}
		ret = append(ret, *next)
	}

	if !validators.Validate() {
		return nil, fmt.Errorf("Index hashes don't match!")
	}

	return ret, nil
}

// }}}

// }}}

// getFile wrapper {{{

func (a Archive) getFile(requestPath string, tee io.Writer) (io.Reader, Closer, error) {
	archivePath := a.root + "/" + requestPath
	reader, closer, err := a.pathReader(archivePath)
	if err != nil {
		return nil, nil, err
	}

	reader, err := compression.Decompress(reader, archivePath, tee)
	if err != nil {
		closer()
	}
	return reader, err

}

// }}}

// Constructors {{{

func NewArchive(
	root string,
	pathReader PathReader,
	keyring *openpgp.EntityList,
) Archive {
	return Archive{
		root:       root,
		pathReader: pathReader,
		keyring:    keyring,
	}
}

// Filesystem Archive {{{

func NewFilesystemArchive(root string, keyring *openpgp.EntityList) Archive {
	return NewArchive(root, filesystemPathReader, keyring)
}

func NewHttpArchive(root string, keyring *openpgp.EntityList) Archive {
	return NewArchive(root, httpPathReader, keyring)
}

// }}}

// }}}

// Readers {{{

func filesystemPathReader(path string) (io.Reader, Closer, error) {
	fd, err := os.Open(path)
	return fd, fd.Close, err
}

func httpPathReader(uri string) (io.Reader, Closer, error) {
	resp, err := http.Get(uri)
	if err != nil {
		return nil, nil, err
	}
	return resp.Body, resp.Body.Close, nil
}

// }}}

// }}}

// vim: foldmethod=marker
<|endoftext|>


<commit_before>package esatompub

import (
	"crypto/rand"
	"database/sql"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/armon/go-metrics"
	"github.com/xtracdev/goes"
	"github.com/xtracdev/orapub"
	"os"
	"strconv"
	"time"
)

const defaultFeedThreshold = 100

const (
	sqlLatestFeedId        = `select feedid from feed where id = (select max(id) from feed)`
	sqlInsertEventIntoFeed = `insert into atom_event (aggregate_id, version,typecode, payload) values(:1,:2,:3,:4)`
	sqlRecentFeedCount = `select count(*) from atom_event where feedid is null`
	sqlUpdateFeedIds = `update atom_event set feedid = :1 where feedid is null`
	sqlInsertFeed = `insert into feed (feedid, previous) values (:1, :2)`
	sqlLockTable = `lock table feed in exclusive mode`
)

var FeedThreshold = defaultFeedThreshold

func logDatabaseTimingStats(sql string, start time.Time, err error) {
	duration := time.Now().Sub(start)
	go func(sql string, duration time.Duration, err error) {
		ms := float32(duration.Nanoseconds()) / 1000.0 / 1000.0
		if err != nil {
			key := []string{"es-atom-data", "db", fmt.Sprintf("%s-error", sql)}
			metrics.AddSample(key, float32(ms))
			metrics.IncrCounter(key, 1)
		} else {
			key := []string{"es-atom-data", "db", sql}
			metrics.AddSample(key, float32(ms))
			metrics.IncrCounter(key, 1)
		}
	}(sql,duration,err)
}

func writeProcessEventStats(start time.Time, err error) {
	duration := time.Now().Sub(start)
	go func(duration time.Duration, err error) {
		ms := float32(duration.Nanoseconds()) / 1000.0 / 1000.0
		if err != nil {
			key := []string{"es-atom-data", "process-event", "error"}
			metrics.AddSample(key, float32(ms))
			metrics.IncrCounter(key, 1)
		} else {
			key := []string{"es-atom-data", "process-event", "ok"}
			metrics.AddSample(key, float32(ms))
			metrics.IncrCounter(key, 1)
		}
	}(duration,err)
}

func ReadFeedThresholdFromEnv() {
	thresholdOverride := os.Getenv("FEED_THRESHOLD")
	if thresholdOverride != "" {
		threshold, err := strconv.Atoi(thresholdOverride)
		if err != nil {
			log.Warnf("Attempted to override threshold with non integer: %s", thresholdOverride)
			log.Warnf("Defaulting to %d", defaultFeedThreshold)
			FeedThreshold = defaultFeedThreshold
			return
		}

		log.Infof("Overriding default feed threshold with %d", threshold)
		FeedThreshold = threshold
	}
}

func selectLatestFeed(tx *sql.Tx) (sql.NullString, error) {
	log.Debug("Select last feed id")

	var feedid sql.NullString
	start := time.Now()
	rows, err := tx.Query(sqlLatestFeedId)
	if err != nil {
		logDatabaseTimingStats("sqlLatestFeedId", start, err)
		return feedid, err
	}

	defer rows.Close()
	for rows.Next() {
		//Only one row can be returned at most
		if err = rows.Scan(&feedid); err != nil {
			logDatabaseTimingStats("sqlLatestFeedId", start, err)
			return feedid, err
		}
	}

	logDatabaseTimingStats("sqlLatestFeedId", start, err)
	return feedid, nil
}

func writeEventToAtomEventTable(tx *sql.Tx, event *goes.Event) error {
	log.Debug("insert event into atom_event")
	start := time.Now()
	_, err := tx.Exec(sqlInsertEventIntoFeed,
		event.Source, event.Version, event.TypeCode, event.Payload)
	logDatabaseTimingStats("sqlInsertEventIntoFeed", start, err)
	return err
}

func getRecentFeedCount(tx *sql.Tx) (int, error) {
	log.Debug("get current count")
	var count int
	start := time.Now()
	err := tx.QueryRow(sqlRecentFeedCount).Scan(&count)
	logDatabaseTimingStats("sqlRecentFeedCount", start, err)

	return count, err
}

func createNewFeed(tx *sql.Tx, currentFeedId sql.NullString) error {
	log.Infof("Feed threshold of %d met", FeedThreshold)
	var prevFeedId sql.NullString
	uuidStr, err := uuid()
	if err != nil {
		return err
	}

	if currentFeedId.Valid {
		prevFeedId = currentFeedId

	}
	currentFeedId = sql.NullString{String: uuidStr, Valid: true}

	log.Info("Update feed ids")

	start := time.Now()
	_, err = tx.Exec(sqlUpdateFeedIds, currentFeedId)
	logDatabaseTimingStats("sqlUpdateFeedIds", start, err)

	if err != nil {
		return err
	}

	log.Infof("Insert into feed %v, %v", currentFeedId, prevFeedId)
	start = time.Now()
	_, err = tx.Exec(sqlInsertFeed,
		currentFeedId, prevFeedId)
	logDatabaseTimingStats("sqlInsertFeed", start, err)
	return err
}

func lockTable(tx *sql.Tx) error {
	start := time.Now()
	_, err := tx.Exec(sqlLockTable)
	logDatabaseTimingStats("sqlLockTable", start, err)
	return err
}

func processEvent(db *sql.DB, event *goes.Event) error {
	log.Debug("Processor invoked")

	//Need a transaction to group the work in this method
	log.Debug("create transaction")
	tx, err := db.Begin()
	if err != nil {
		return err
	}

	defer tx.Rollback()

	//Treat the processing as a critical section to avoid concurrency headaches.
	err = lockTable(tx)
	if err != nil {
		return err
	}

	//Get the current feed id
	feedid, err := selectLatestFeed(tx)
	if err != nil {
		return err
	}
	log.Debugf("previous feed id is %s", feedid.String)

	//Insert current row
	err = writeEventToAtomEventTable(tx, event)
	if err != nil {
		return err
	}

	//Get current count of records in the current feed
	count, err := getRecentFeedCount(tx)
	if err != nil {
		return err
	}
	log.Debugf("current count is %d", count)

	//Threshold met
	if count == FeedThreshold {
		err := createNewFeed(tx, feedid)
		if err != nil {
			return err
		}
	}

	log.Debug("commit txn")
	tx.Commit()

	return nil
}

func configureStatsD() {
	statsdEndpoint := os.Getenv("STATSD_ENDPOINT")
	log.Infof("STATSD_ENDPOINT: %s", statsdEndpoint)

	if statsdEndpoint != "" {

		log.Info("Using vanilla statsd client to send telemetry to ", statsdEndpoint)
		sink, err := metrics.NewStatsdSink(statsdEndpoint)
		if err != nil {
			log.Warn("Unable to configure statds sink", err.Error())
			return
		}
		metrics.NewGlobal(metrics.DefaultConfig(statsdEndpoint), sink)
	} else {
		log.Info("Using in memory metrics accumulator - dump via USR1 signal")
		inm := metrics.NewInmemSink(10*time.Second, 5*time.Minute)
		metrics.DefaultInmemSignal(inm)
		metrics.NewGlobal(metrics.DefaultConfig("xavi"), inm)
	}
}

func NewESAtomPubProcessor() orapub.EventProcessor {
	configureStatsD()
	return orapub.EventProcessor{
		Initialize: func(db *sql.DB) error {
			return nil
		},
		Processor: func(db *sql.DB, event *goes.Event) error {
			start := time.Now()
			err :=  processEvent(db, event)
			writeProcessEventStats(start, err)
			return err
		},
	}
}

func uuid() (string, error) {
	b := make([]byte, 16)
	_, err := rand.Read(b)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]), nil

}
<commit_msg>Added more precise control of transaction commit and rollback<commit_after>package esatompub

import (
	"crypto/rand"
	"database/sql"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/armon/go-metrics"
	"github.com/xtracdev/goes"
	"github.com/xtracdev/orapub"
	"os"
	"strconv"
	"time"
)

const defaultFeedThreshold = 100

const (
	sqlLatestFeedId        = `select feedid from feed where id = (select max(id) from feed)`
	sqlInsertEventIntoFeed = `insert into atom_event (aggregate_id, version,typecode, payload) values(:1,:2,:3,:4)`
	sqlRecentFeedCount = `select count(*) from atom_event where feedid is null`
	sqlUpdateFeedIds = `update atom_event set feedid = :1 where feedid is null`
	sqlInsertFeed = `insert into feed (feedid, previous) values (:1, :2)`
	sqlLockTable = `lock table feed in exclusive mode`
)

var FeedThreshold = defaultFeedThreshold

func logDatabaseTimingStats(sql string, start time.Time, err error) {
	duration := time.Now().Sub(start)
	go func(sql string, duration time.Duration, err error) {
		ms := float32(duration.Nanoseconds()) / 1000.0 / 1000.0
		if err != nil {
			key := []string{"es-atom-data", "db", fmt.Sprintf("%s-error", sql)}
			metrics.AddSample(key, float32(ms))
			metrics.IncrCounter(key, 1)
		} else {
			key := []string{"es-atom-data", "db", sql}
			metrics.AddSample(key, float32(ms))
			metrics.IncrCounter(key, 1)
		}
	}(sql,duration,err)
}

func writeProcessEventStats(start time.Time, err error) {
	duration := time.Now().Sub(start)
	go func(duration time.Duration, err error) {
		ms := float32(duration.Nanoseconds()) / 1000.0 / 1000.0
		if err != nil {
			key := []string{"es-atom-data", "process-event", "error"}
			metrics.AddSample(key, float32(ms))
			metrics.IncrCounter(key, 1)
		} else {
			key := []string{"es-atom-data", "process-event", "ok"}
			metrics.AddSample(key, float32(ms))
			metrics.IncrCounter(key, 1)
		}
	}(duration,err)
}

func ReadFeedThresholdFromEnv() {
	thresholdOverride := os.Getenv("FEED_THRESHOLD")
	if thresholdOverride != "" {
		threshold, err := strconv.Atoi(thresholdOverride)
		if err != nil {
			log.Warnf("Attempted to override threshold with non integer: %s", thresholdOverride)
			log.Warnf("Defaulting to %d", defaultFeedThreshold)
			FeedThreshold = defaultFeedThreshold
			return
		}

		log.Infof("Overriding default feed threshold with %d", threshold)
		FeedThreshold = threshold
	}
}

func selectLatestFeed(tx *sql.Tx) (sql.NullString, error) {
	log.Debug("Select last feed id")

	var feedid sql.NullString
	start := time.Now()
	rows, err := tx.Query(sqlLatestFeedId)
	if err != nil {
		logDatabaseTimingStats("sqlLatestFeedId", start, err)
		return feedid, err
	}

	defer rows.Close()
	for rows.Next() {
		//Only one row can be returned at most
		if err = rows.Scan(&feedid); err != nil {
			logDatabaseTimingStats("sqlLatestFeedId", start, err)
			return feedid, err
		}
	}

	logDatabaseTimingStats("sqlLatestFeedId", start, err)
	return feedid, nil
}

func writeEventToAtomEventTable(tx *sql.Tx, event *goes.Event) error {
	log.Debug("insert event into atom_event")
	start := time.Now()
	_, err := tx.Exec(sqlInsertEventIntoFeed,
		event.Source, event.Version, event.TypeCode, event.Payload)
	logDatabaseTimingStats("sqlInsertEventIntoFeed", start, err)
	return err
}

func getRecentFeedCount(tx *sql.Tx) (int, error) {
	log.Debug("get current count")
	var count int
	start := time.Now()
	err := tx.QueryRow(sqlRecentFeedCount).Scan(&count)
	logDatabaseTimingStats("sqlRecentFeedCount", start, err)

	return count, err
}

func createNewFeed(tx *sql.Tx, currentFeedId sql.NullString) error {
	log.Infof("Feed threshold of %d met", FeedThreshold)
	var prevFeedId sql.NullString
	uuidStr, err := uuid()
	if err != nil {
		return err
	}

	if currentFeedId.Valid {
		prevFeedId = currentFeedId

	}
	currentFeedId = sql.NullString{String: uuidStr, Valid: true}

	log.Info("Update feed ids")

	start := time.Now()
	_, err = tx.Exec(sqlUpdateFeedIds, currentFeedId)
	logDatabaseTimingStats("sqlUpdateFeedIds", start, err)

	if err != nil {
		return err
	}

	log.Infof("Insert into feed %v, %v", currentFeedId, prevFeedId)
	start = time.Now()
	_, err = tx.Exec(sqlInsertFeed,
		currentFeedId, prevFeedId)
	logDatabaseTimingStats("sqlInsertFeed", start, err)
	return err
}

func lockTable(tx *sql.Tx) error {
	start := time.Now()
	_, err := tx.Exec(sqlLockTable)
	logDatabaseTimingStats("sqlLockTable", start, err)
	return err
}

func doRollback(tx *sql.Tx) {
	err := tx.Rollback()
	if err != nil {
		log.Warnf("Error on transaction rollback: %s",err.Error())
	}
}

func processEvent(db *sql.DB, event *goes.Event) error {
	log.Debug("Processor invoked")

	//Need a transaction to group the work in this method
	log.Debug("create transaction")
	tx, err := db.Begin()
	if err != nil {
		return err
	}

	//Treat the processing as a critical section to avoid concurrency headaches.
	err = lockTable(tx)
	if err != nil {
		doRollback(tx)
		return err
	}

	//Get the current feed id
	feedid, err := selectLatestFeed(tx)
	if err != nil {
		doRollback(tx)
		return err
	}
	log.Debugf("previous feed id is %s", feedid.String)

	//Insert current row
	err = writeEventToAtomEventTable(tx, event)
	if err != nil {
		doRollback(tx)
		return err
	}

	//Get current count of records in the current feed
	count, err := getRecentFeedCount(tx)
	if err != nil {
		doRollback(tx)
		return err
	}
	log.Debugf("current count is %d", count)

	//Threshold met
	if count == FeedThreshold {
		err := createNewFeed(tx, feedid)
		if err != nil {
			doRollback(tx)
			return err
		}
	}

	log.Debug("commit txn")
	err = tx.Commit()
	if err != nil {
		return err
	}

	return nil
}

func configureStatsD() {
	statsdEndpoint := os.Getenv("STATSD_ENDPOINT")
	log.Infof("STATSD_ENDPOINT: %s", statsdEndpoint)

	if statsdEndpoint != "" {

		log.Info("Using vanilla statsd client to send telemetry to ", statsdEndpoint)
		sink, err := metrics.NewStatsdSink(statsdEndpoint)
		if err != nil {
			log.Warn("Unable to configure statds sink", err.Error())
			return
		}
		metrics.NewGlobal(metrics.DefaultConfig(statsdEndpoint), sink)
	} else {
		log.Info("Using in memory metrics accumulator - dump via USR1 signal")
		inm := metrics.NewInmemSink(10*time.Second, 5*time.Minute)
		metrics.DefaultInmemSignal(inm)
		metrics.NewGlobal(metrics.DefaultConfig("xavi"), inm)
	}
}

func NewESAtomPubProcessor() orapub.EventProcessor {
	configureStatsD()
	return orapub.EventProcessor{
		Initialize: func(db *sql.DB) error {
			return nil
		},
		Processor: func(db *sql.DB, event *goes.Event) error {
			start := time.Now()
			err :=  processEvent(db, event)
			writeProcessEventStats(start, err)
			return err
		},
	}
}

func uuid() (string, error) {
	b := make([]byte, 16)
	_, err := rand.Read(b)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]), nil

}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"math/rand"
	"strings"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/mozilla-services/reaper/events"
	"github.com/mozilla-services/reaper/reapable"
	log "github.com/mozilla-services/reaper/reaperlog"
)

const (
	reaperTag           = "REAPER"
	reaperTagSeparator  = "|"
	reaperTagTimeFormat = "2006-01-02 03:04PM MST"
)

var (
	config  *AWSConfig
	timeout = time.Tick(100 * time.Millisecond)
)

type AWSConfig struct {
	Notifications    events.NotificationsConfig
	HTTP             events.HTTPConfig
	Regions          []string
	WhitelistTag     string
	DefaultOwner     string
	DefaultEmailHost string
	DryRun           bool
}

func NewAWSConfig() *AWSConfig {
	return &AWSConfig{}
}

func SetAWSConfig(c *AWSConfig) {
	config = c
}

// convenience function that returns a map of instances in ASGs
func AllASGInstanceIDs(as []*AutoScalingGroup) map[reapable.Region]map[reapable.ID]bool {
	// maps region to id to bool
	inASG := make(map[reapable.Region]map[reapable.ID]bool)
	for _, region := range config.Regions {
		inASG[reapable.Region(region)] = make(map[reapable.ID]bool)
	}
	for _, a := range as {
		for _, instanceID := range a.Instances {
			// add the instance to the map
			inASG[a.Region][instanceID] = true
		}
	}
	return inASG
}

func AllCloudformations() chan *Cloudformation {
	ch := make(chan *Cloudformation)
	// waitgroup for all regions
	wg := sync.WaitGroup{}
	for _, region := range config.Regions {
		wg.Add(1)
		go func(region string) {
			// add region to waitgroup
			api := cloudformation.New(&aws.Config{Region: region})
			err := api.DescribeStacksPages(&cloudformation.DescribeStacksInput{}, func(resp *cloudformation.DescribeStacksOutput, lastPage bool) bool {
				for _, stack := range resp.Stacks {
					ch <- NewCloudformation(region, stack)
				}
				// if we are at the last page, we should not continue
				// the return value of this func is "shouldContinue"
				if lastPage {
					// on the last page, finish this region
					wg.Done()
					return false
				}
				return true
			})
			if err != nil {
				// probably should do something here...
				log.Error(err.Error())
				// don't wait if the API call failed
				wg.Done()
			}
		}(region)
	}
	go func() {
		// in a separate goroutine, wait for all regions to finish
		// when they finish, close the chan
		wg.Wait()
		close(ch)

	}()
	return ch
}

func CloudformationResources(c Cloudformation) chan *cloudformation.StackResource {
	ch := make(chan *cloudformation.StackResource)
	api := cloudformation.New(&aws.Config{Region: string(c.Region)})
	// TODO: stupid
	stringName := string(c.ID)

	go func() {
		<-timeout

		// this query can fail, so we retry
		didRetry := false
		input := &cloudformation.DescribeStackResourcesInput{StackName: &stringName}

		// initial query
		resp, err := api.DescribeStackResources(input)
		for err != nil {
			sleepTime := 2*time.Second + time.Duration(rand.Intn(2000))*time.Millisecond
			if err != nil {
				// this error is annoying and will come up all the time... so you can disable it
				if strings.Split(err.Error(), ":")[0] == "Throttling" && log.Extras() {
					log.Warning(fmt.Sprintf("StackResources: %s (retrying %s after %ds)", err.Error(), c.ID, sleepTime*1.0/time.Second))
				} else if strings.Split(err.Error(), ":")[0] != "Throttling" {
					// any other errors
					log.Error(fmt.Sprintf("StackResources: %s (retrying %s after %ds)", err.Error(), c.ID, sleepTime*1.0/time.Second))
				}
			}

			// wait a random amount of time... hopefully long enough to beat rate limiting
			time.Sleep(sleepTime)

			// retry query
			resp, err = api.DescribeStackResources(input)
			didRetry = true
		}
		if didRetry && log.Extras() {
			log.Notice("Retry succeeded for %s!", c.ID)
		}
		for _, resource := range resp.StackResources {
			ch <- resource
		}
		close(ch)
	}()
	return ch
}

func ASGInstanceIDs(a *AutoScalingGroup) map[reapable.Region]map[reapable.ID]bool {
	// maps region to id to bool
	inASG := make(map[reapable.Region]map[reapable.ID]bool)
	for _, region := range config.Regions {
		inASG[reapable.Region(region)] = make(map[reapable.ID]bool)
	}
	for _, instanceID := range a.Instances {
		// add the instance to the map
		inASG[a.Region][instanceID] = true
	}
	return inASG
}

// AllAutoScalingGroups describes every AutoScalingGroup in the requested regions
// *AutoScalingGroups are created for every *autoscaling.AutoScalingGroup
// and are passed to a channel
func AllAutoScalingGroups() chan *AutoScalingGroup {
	ch := make(chan *AutoScalingGroup)
	// waitgroup for all regions
	wg := sync.WaitGroup{}
	for _, region := range config.Regions {
		wg.Add(1)
		go func(region string) {
			// add region to waitgroup
			api := autoscaling.New(&aws.Config{Region: region})
			err := api.DescribeAutoScalingGroupsPages(&autoscaling.DescribeAutoScalingGroupsInput{}, func(resp *autoscaling.DescribeAutoScalingGroupsOutput, lastPage bool) bool {
				for _, asg := range resp.AutoScalingGroups {
					ch <- NewAutoScalingGroup(region, asg)
				}
				// if we are at the last page, we should not continue
				// the return value of this func is "shouldContinue"
				if lastPage {
					// on the last page, finish this region
					wg.Done()
					return false
				}
				return true
			})
			if err != nil {
				// probably should do something here...
				log.Error(err.Error())
				// don't wait if the API call failed
				wg.Done()
			}
		}(region)
	}
	go func() {
		// in a separate goroutine, wait for all regions to finish
		// when they finish, close the chan
		wg.Wait()
		close(ch)

	}()
	return ch
}

// AllInstances describes every instance in the requested regions
// *Instances are created for each *ec2.Instance
// and are passed to a channel
func AllInstances() chan *Instance {
	ch := make(chan *Instance)
	// waitgroup for all regions
	wg := sync.WaitGroup{}
	for _, region := range config.Regions {
		wg.Add(1)
		go func(region string) {
			// add region to waitgroup
			api := ec2.New(&aws.Config{Region: region})
			// DescribeInstancesPages does autopagination
			err := api.DescribeInstancesPages(&ec2.DescribeInstancesInput{}, func(resp *ec2.DescribeInstancesOutput, lastPage bool) bool {
				for _, res := range resp.Reservations {
					for _, instance := range res.Instances {
						ch <- NewInstance(region, instance)
					}
				}
				// if we are at the last page, we should not continue
				// the return value of this func is "shouldContinue"
				if lastPage {
					wg.Done()
					return false
				}
				return true
			})
			if err != nil {
				// probably should do something here...
				log.Error(err.Error())
				// don't wait if the API call failed
				wg.Done()
			}
		}(region)
	}
	go func() {
		// in a separate goroutine, wait for all regions to finish
		// when they finish, close the chan
		wg.Wait()
		close(ch)
	}()
	return ch
}

func AllSecurityGroups() chan *SecurityGroup {
	ch := make(chan *SecurityGroup)
	// waitgroup for all regions
	wg := sync.WaitGroup{}
	for _, region := range config.Regions {
		wg.Add(1)
		go func(region string) {
			// add region to waitgroup
			api := ec2.New(&aws.Config{Region: region})
			resp, err := api.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{})
			for _, sg := range resp.SecurityGroups {
				ch <- NewSecurityGroup(region, sg)
			}
			if err != nil {
				// probably should do something here...
				log.Error(err.Error())
			}
			wg.Done()
		}(region)
	}
	go func() {
		// in a separate goroutine, wait for all regions to finish
		// when they finish, close the chan
		wg.Wait()
		close(ch)

	}()
	return ch
}
<commit_msg>remove AllAutoScalingGroupInstanceIDs<commit_after>package aws

import (
	"fmt"
	"math/rand"
	"strings"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/mozilla-services/reaper/events"
	"github.com/mozilla-services/reaper/reapable"
	log "github.com/mozilla-services/reaper/reaperlog"
)

const (
	reaperTag           = "REAPER"
	reaperTagSeparator  = "|"
	reaperTagTimeFormat = "2006-01-02 03:04PM MST"
)

var (
	config  *AWSConfig
	timeout = time.Tick(100 * time.Millisecond)
)

type AWSConfig struct {
	Notifications    events.NotificationsConfig
	HTTP             events.HTTPConfig
	Regions          []string
	WhitelistTag     string
	DefaultOwner     string
	DefaultEmailHost string
	DryRun           bool
}

func NewAWSConfig() *AWSConfig {
	return &AWSConfig{}
}

func SetAWSConfig(c *AWSConfig) {
	config = c
}

func AllCloudformations() chan *Cloudformation {
	ch := make(chan *Cloudformation)
	// waitgroup for all regions
	wg := sync.WaitGroup{}
	for _, region := range config.Regions {
		wg.Add(1)
		go func(region string) {
			// add region to waitgroup
			api := cloudformation.New(&aws.Config{Region: region})
			err := api.DescribeStacksPages(&cloudformation.DescribeStacksInput{}, func(resp *cloudformation.DescribeStacksOutput, lastPage bool) bool {
				for _, stack := range resp.Stacks {
					ch <- NewCloudformation(region, stack)
				}
				// if we are at the last page, we should not continue
				// the return value of this func is "shouldContinue"
				if lastPage {
					// on the last page, finish this region
					wg.Done()
					return false
				}
				return true
			})
			if err != nil {
				// probably should do something here...
				log.Error(err.Error())
				// don't wait if the API call failed
				wg.Done()
			}
		}(region)
	}
	go func() {
		// in a separate goroutine, wait for all regions to finish
		// when they finish, close the chan
		wg.Wait()
		close(ch)

	}()
	return ch
}

func CloudformationResources(c Cloudformation) chan *cloudformation.StackResource {
	ch := make(chan *cloudformation.StackResource)
	api := cloudformation.New(&aws.Config{Region: string(c.Region)})
	// TODO: stupid
	stringName := string(c.ID)

	go func() {
		<-timeout

		// this query can fail, so we retry
		didRetry := false
		input := &cloudformation.DescribeStackResourcesInput{StackName: &stringName}

		// initial query
		resp, err := api.DescribeStackResources(input)
		for err != nil {
			sleepTime := 2*time.Second + time.Duration(rand.Intn(2000))*time.Millisecond
			if err != nil {
				// this error is annoying and will come up all the time... so you can disable it
				if strings.Split(err.Error(), ":")[0] == "Throttling" && log.Extras() {
					log.Warning(fmt.Sprintf("StackResources: %s (retrying %s after %ds)", err.Error(), c.ID, sleepTime*1.0/time.Second))
				} else if strings.Split(err.Error(), ":")[0] != "Throttling" {
					// any other errors
					log.Error(fmt.Sprintf("StackResources: %s (retrying %s after %ds)", err.Error(), c.ID, sleepTime*1.0/time.Second))
				}
			}

			// wait a random amount of time... hopefully long enough to beat rate limiting
			time.Sleep(sleepTime)

			// retry query
			resp, err = api.DescribeStackResources(input)
			didRetry = true
		}
		if didRetry && log.Extras() {
			log.Notice("Retry succeeded for %s!", c.ID)
		}
		for _, resource := range resp.StackResources {
			ch <- resource
		}
		close(ch)
	}()
	return ch
}

func ASGInstanceIDs(a *AutoScalingGroup) map[reapable.Region]map[reapable.ID]bool {
	// maps region to id to bool
	inASG := make(map[reapable.Region]map[reapable.ID]bool)
	for _, region := range config.Regions {
		inASG[reapable.Region(region)] = make(map[reapable.ID]bool)
	}
	for _, instanceID := range a.Instances {
		// add the instance to the map
		inASG[a.Region][instanceID] = true
	}
	return inASG
}

// AllAutoScalingGroups describes every AutoScalingGroup in the requested regions
// *AutoScalingGroups are created for every *autoscaling.AutoScalingGroup
// and are passed to a channel
func AllAutoScalingGroups() chan *AutoScalingGroup {
	ch := make(chan *AutoScalingGroup)
	// waitgroup for all regions
	wg := sync.WaitGroup{}
	for _, region := range config.Regions {
		wg.Add(1)
		go func(region string) {
			// add region to waitgroup
			api := autoscaling.New(&aws.Config{Region: region})
			err := api.DescribeAutoScalingGroupsPages(&autoscaling.DescribeAutoScalingGroupsInput{}, func(resp *autoscaling.DescribeAutoScalingGroupsOutput, lastPage bool) bool {
				for _, asg := range resp.AutoScalingGroups {
					ch <- NewAutoScalingGroup(region, asg)
				}
				// if we are at the last page, we should not continue
				// the return value of this func is "shouldContinue"
				if lastPage {
					// on the last page, finish this region
					wg.Done()
					return false
				}
				return true
			})
			if err != nil {
				// probably should do something here...
				log.Error(err.Error())
				// don't wait if the API call failed
				wg.Done()
			}
		}(region)
	}
	go func() {
		// in a separate goroutine, wait for all regions to finish
		// when they finish, close the chan
		wg.Wait()
		close(ch)

	}()
	return ch
}

// AllInstances describes every instance in the requested regions
// *Instances are created for each *ec2.Instance
// and are passed to a channel
func AllInstances() chan *Instance {
	ch := make(chan *Instance)
	// waitgroup for all regions
	wg := sync.WaitGroup{}
	for _, region := range config.Regions {
		wg.Add(1)
		go func(region string) {
			// add region to waitgroup
			api := ec2.New(&aws.Config{Region: region})
			// DescribeInstancesPages does autopagination
			err := api.DescribeInstancesPages(&ec2.DescribeInstancesInput{}, func(resp *ec2.DescribeInstancesOutput, lastPage bool) bool {
				for _, res := range resp.Reservations {
					for _, instance := range res.Instances {
						ch <- NewInstance(region, instance)
					}
				}
				// if we are at the last page, we should not continue
				// the return value of this func is "shouldContinue"
				if lastPage {
					wg.Done()
					return false
				}
				return true
			})
			if err != nil {
				// probably should do something here...
				log.Error(err.Error())
				// don't wait if the API call failed
				wg.Done()
			}
		}(region)
	}
	go func() {
		// in a separate goroutine, wait for all regions to finish
		// when they finish, close the chan
		wg.Wait()
		close(ch)
	}()
	return ch
}

func AllSecurityGroups() chan *SecurityGroup {
	ch := make(chan *SecurityGroup)
	// waitgroup for all regions
	wg := sync.WaitGroup{}
	for _, region := range config.Regions {
		wg.Add(1)
		go func(region string) {
			// add region to waitgroup
			api := ec2.New(&aws.Config{Region: region})
			resp, err := api.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{})
			for _, sg := range resp.SecurityGroups {
				ch <- NewSecurityGroup(region, sg)
			}
			if err != nil {
				// probably should do something here...
				log.Error(err.Error())
			}
			wg.Done()
		}(region)
	}
	go func() {
		// in a separate goroutine, wait for all regions to finish
		// when they finish, close the chan
		wg.Wait()
		close(ch)

	}()
	return ch
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"strconv"

	awssdk "github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/elbv2"

	"github.com/gliderlabs/registrator/bridge"
	eureka "github.com/hudl/fargo"
)

// LBInfo represents a ELBv2 endpoint
type LBInfo struct {
	DNSName string
	Port    int64
}

var lbCache = make(map[string]*LBInfo)
var registrations = make(map[string]bool)

// Helper function to retrieve all target groups
func getAllTargetGroups(svc *elbv2.ELBV2) ([]*elbv2.DescribeTargetGroupsOutput, error) {
	var tgs []*elbv2.DescribeTargetGroupsOutput
	var e error
	var mark *string

	// Get first page of groups
	tg, e := getTargetGroupsPage(svc, mark)

	if e != nil {
		return nil, e
	}
	tgs = append(tgs, tg)
	mark = tg.NextMarker

	// Page through all remaining target groups generating a slice of DescribeTargetGroupOutputs
	for mark != nil {
		tg, e = getTargetGroupsPage(svc, mark)
		tgs = append(tgs, tg)
		mark = tg.NextMarker
		if e != nil {
			return nil, e
		}
	}
	return tgs, e
}

// Helper function to get a page of target groups
func getTargetGroupsPage(svc *elbv2.ELBV2, marker *string) (*elbv2.DescribeTargetGroupsOutput, error) {
	params := &elbv2.DescribeTargetGroupsInput{
		PageSize: awssdk.Int64(400),
		Marker:   marker,
	}
	tg, e := svc.DescribeTargetGroups(params)

	if e != nil {
		log.Printf("An error occurred using DescribeTargetGroups: %s \n", e.Error())
		return nil, e
	}
	return tg, nil
}

// GetELBV2ForContainer returns an LBInfo struct with the load balancer DNS name and listener port for a given instanceId and port
// if an error occurs, or the target is not found, an empty LBInfo is returned.
// Return the DNS:port pair as an identifier to put in the container's registration metadata
// Pass it the instanceID for the docker host, and the the host port to lookup the associated ELB.
// useCache parameter, if true, will retrieve ELBv2 details from memory, rather than calling AWS.
// this is only really safe to use for heartbeat calls, as details can change dynamically
func GetELBV2ForContainer(containerID string, instanceID string, port int64, useCache bool) (lbinfo *LBInfo, err error) {

	// Retrieve from basic cache (for heartbeats)
	cacheKey := containerID
	if val, ok := lbCache[cacheKey]; ok && useCache {
		log.Println("Retrieving value from cache.")
		return val, nil
	}

	var lbArns []*string
	var lbPort *int64
	var tgArn string
	info := &LBInfo{}

	sess, err := session.NewSession()
	if err != nil {
		message := fmt.Errorf("Failed to create session connecting to AWS: %s", err)
		return nil, message
	}

	// Need to set the region here - we'll get it from instance metadata
	awsMetadata := GetMetadata()
	svc := elbv2.New(sess, awssdk.NewConfig().WithRegion(awsMetadata.Region))

	// TODO Note: There could be thousands of these, and we need to check them all.  Seems to be no
	// other way to retrieve a TG via instance/port with current API
	tgslice, err := getAllTargetGroups(svc)
	if err != nil || tgslice == nil {
		message := fmt.Errorf("Failed to retrieve Target Groups: %s", err)
		return nil, message
	}

	// Check each target group's target list for a matching port and instanceID
	// TODO Assumption: that that there is only one LB for the target group (though the data structure allows more)
	for _, tgs := range tgslice {
		for _, tg := range tgs.TargetGroups {

			thParams := &elbv2.DescribeTargetHealthInput{
				TargetGroupArn: awssdk.String(*tg.TargetGroupArn),
			}

			tarH, err := svc.DescribeTargetHealth(thParams)
			if err != nil {
				log.Printf("An error occurred using DescribeTargetHealth: %s \n", err.Error())
				return nil, err
			}

			for _, thd := range tarH.TargetHealthDescriptions {
				if *thd.Target.Port == port && *thd.Target.Id == instanceID {
					lbArns = tg.LoadBalancerArns
					tgArn = *tg.TargetGroupArn
					break
				}
			}
		}
		if lbArns != nil && tgArn != "" {
			break
		}
	}

	if err != nil || lbArns == nil {
		message := fmt.Errorf("failed to retrieve load balancer ARN")
		return nil, message
	}

	// Loop through the load balancer listeners to get the listener port for the target group
	lsnrParams := &elbv2.DescribeListenersInput{
		LoadBalancerArn: lbArns[0],
	}
	lnrData, err := svc.DescribeListeners(lsnrParams)
	if err != nil {
		log.Printf("An error occurred using DescribeListeners: %s \n", err.Error())
		return nil, err
	}
	for _, listener := range lnrData.Listeners {
		for _, act := range listener.DefaultActions {
			if *act.TargetGroupArn == tgArn {
				log.Printf("Found matching listener: %v", *listener.ListenerArn)
				lbPort = listener.Port
				break
			}
		}
	}
	if lbPort == nil {
		message := fmt.Errorf("error: Unable to identify listener port for ELBv2")
		return nil, message
	}

	// Get more information on the load balancer to retrieve the DNSName
	lbParams := &elbv2.DescribeLoadBalancersInput{
		LoadBalancerArns: lbArns,
	}
	lbData, err := svc.DescribeLoadBalancers(lbParams)
	if err != nil {
		log.Printf("An error occurred using DescribeLoadBalancers: %s \n", err.Error())
		return nil, err
	}
	log.Printf("LB Endpoint for Instance:%v Port:%v, Target Group:%v, is: %s:%s\n", instanceID, port, tgArn, *lbData.LoadBalancers[0].DNSName, strconv.FormatInt(*lbPort, 10))

	info.DNSName = *lbData.LoadBalancers[0].DNSName
	info.Port = *lbPort

	// Add to a basic cache for heartbeats
	lbCache[cacheKey] = info

	return info, nil
}

// CheckELBFlags - Helper function to check if the correct config flags are set to use ELBs
func CheckELBFlags(service *bridge.Service) bool {
	if service.Attrs["eureka_use_elbv2_endpoint"] != "" && service.Attrs["eureka_datacenterinfo_name"] != eureka.MyOwn {
		v, err := strconv.ParseBool(service.Attrs["eureka_use_elbv2_endpoint"])
		if err != nil {
			log.Printf("eureka: eureka_use_elbv2_endpoint must be valid boolean, was %v : %s", v, err)
			return false
		}
		return v
	}
	return false
}

// Helper function to create a registration struct, and change container registration
// useCache parameter is passed to getELBV2ForContainer
func setRegInfo(service *bridge.Service, registration *eureka.Instance, useCache bool) *eureka.Instance {

	awsMetadata := GetMetadata()

	elbMetadata, err := GetELBV2ForContainer(service.Origin.ContainerID, awsMetadata.InstanceID, int64(registration.Port), useCache)

	if err != nil {
		log.Printf("Unable to find associated ELBv2 for: %s, Error: %s\n", registration.HostName, err)
		return nil
	}

	elbStrPort := strconv.FormatInt(elbMetadata.Port, 10)
	elbEndpoint := elbMetadata.DNSName + "_" + elbStrPort

	registration.SetMetadataString("has-elbv2", "true")
	registration.SetMetadataString("elbv2-endpoint", elbEndpoint)

	elbReg := new(eureka.Instance)

	// Put a little metadata in here as required - setting InstanceID to the ELB endpoint prevents double registration
	elbReg.DataCenterInfo.Metadata = eureka.AmazonMetadataType{
		PublicHostname: elbMetadata.DNSName,
		HostName:       elbMetadata.DNSName,
		InstanceID:     elbEndpoint,
	}

	elbReg.Port = int(elbMetadata.Port)
	elbReg.IPAddr = elbMetadata.DNSName
	elbReg.App = service.Name
	elbReg.VipAddress = elbReg.IPAddr
	elbReg.HostName = elbMetadata.DNSName
	elbReg.DataCenterInfo.Name = eureka.Amazon
	elbReg.SetMetadataString("is-elbv2", "true")
	elbReg.Status = eureka.UP
	return elbReg
}

// RegisterELBv2 - If specified, also register an ELBv2 (application load balancer, ALB) endpoint in eureka, and alter service name
// for container registrations.  This will mean traffic is directed to the ALB rather than directly to containers
// though they are still registered in eureka for information purposes
func RegisterELBv2(service *bridge.Service, registration *eureka.Instance, client eureka.EurekaConnection) {
	if CheckELBFlags(service) {
		log.Printf("Found ELBv2 flags, will attempt to register LB for: %s\n", registration.HostName)
		elbReg := setRegInfo(service, registration, false)
		if elbReg != nil {
			err := client.RegisterInstance(elbReg)
			if err == nil {
				registrations[service.Origin.ContainerID] = true
			}
			// We reregister the container again too because ELB adds data to it
			client.ReregisterInstance(registration)
		}
	}
}

// DeregisterELBv2 - If specified, and all containers are gone, also deregister the ELBv2 (application load balancer, ALB) endpoint in eureka.
//
func DeregisterELBv2(service *bridge.Service, albEndpoint string, client eureka.EurekaConnection) {
	if CheckELBFlags(service) {

		if albEndpoint == "" {
			log.Printf("No endpoint available when attempting deregister.  ELBv2 will remain registered. Container: %v, IP: %v Port: %v", service.Origin.ContainerID, service.IP, service.Port)
			return
		}

		// Check if there are any containers around with this ALB still attached
		log.Printf("Found ELBv2 flags, will check if it needs to be deregistered too, for: %v\n", albEndpoint)
		appName := "CONTAINER_" + service.Name

		app, err := client.GetApp(appName)
		if err != nil {
			log.Printf("Unable to retrieve app metadata for %s: %s\n", appName, err)
			delete(registrations, service.Origin.ContainerID)
			delete(lbCache, service.Origin.ContainerID)
			return
		}

		if app != nil {
			for _, instance := range app.Instances {
				val, err := instance.Metadata.GetString("elbv2_endpoint")
				if err == nil && val == albEndpoint {
					log.Printf("Eureka entry still present for one or more ALB linked containers: %s\n", val)
					delete(registrations, service.Origin.ContainerID)
					delete(lbCache, service.Origin.ContainerID)
					return
				}
			}
		}

		if app == nil {
			log.Printf("Removing eureka entry for ELBv2: %s\n", albEndpoint)
			elbReg := new(eureka.Instance)
			elbReg.App = service.Name
			elbReg.HostName = albEndpoint // This uses the full endpoint identifier so eureka can find it to remove
			client.DeregisterInstance(elbReg)
			delete(registrations, service.Origin.ContainerID)
			delete(lbCache, service.Origin.ContainerID)
		}
	}
}

// HeartbeatELBv2 - Send a heartbeat to eureka for this ELBv2 registration.  Every host running registrator will send heartbeats, meaning they will
// be received more frequently than the --ttl-refresh interval if there are multiple hosts running registrator.
//
func HeartbeatELBv2(service *bridge.Service, registration *eureka.Instance, client eureka.EurekaConnection) {
	if CheckELBFlags(service) {
		log.Printf("Heartbeating ELBv2 for container: %s)\n", registration.HostName)

		if reg, ok := registrations[service.Origin.ContainerID]; !ok || !reg {
			log.Printf("ELBv2 failed previous registration.  Attempting register now.")
			RegisterELBv2(service, registration, client)
			// We reregister the container again too because ELB adds data to it
			client.ReregisterInstance(registration)
		}

		elbReg := setRegInfo(service, registration, true) // Can safely use cache when heartbeating
		if elbReg != nil {
			client.HeartBeatInstance(elbReg)
		}
	}
}
<commit_msg>Fix typo in metadata retrieval<commit_after>package aws

import (
	"fmt"
	"log"
	"strconv"

	awssdk "github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/elbv2"

	"github.com/gliderlabs/registrator/bridge"
	eureka "github.com/hudl/fargo"
)

// LBInfo represents a ELBv2 endpoint
type LBInfo struct {
	DNSName string
	Port    int64
}

var lbCache = make(map[string]*LBInfo)
var registrations = make(map[string]bool)

// Helper function to retrieve all target groups
func getAllTargetGroups(svc *elbv2.ELBV2) ([]*elbv2.DescribeTargetGroupsOutput, error) {
	var tgs []*elbv2.DescribeTargetGroupsOutput
	var e error
	var mark *string

	// Get first page of groups
	tg, e := getTargetGroupsPage(svc, mark)

	if e != nil {
		return nil, e
	}
	tgs = append(tgs, tg)
	mark = tg.NextMarker

	// Page through all remaining target groups generating a slice of DescribeTargetGroupOutputs
	for mark != nil {
		tg, e = getTargetGroupsPage(svc, mark)
		tgs = append(tgs, tg)
		mark = tg.NextMarker
		if e != nil {
			return nil, e
		}
	}
	return tgs, e
}

// Helper function to get a page of target groups
func getTargetGroupsPage(svc *elbv2.ELBV2, marker *string) (*elbv2.DescribeTargetGroupsOutput, error) {
	params := &elbv2.DescribeTargetGroupsInput{
		PageSize: awssdk.Int64(400),
		Marker:   marker,
	}
	tg, e := svc.DescribeTargetGroups(params)

	if e != nil {
		log.Printf("An error occurred using DescribeTargetGroups: %s \n", e.Error())
		return nil, e
	}
	return tg, nil
}

// GetELBV2ForContainer returns an LBInfo struct with the load balancer DNS name and listener port for a given instanceId and port
// if an error occurs, or the target is not found, an empty LBInfo is returned.
// Return the DNS:port pair as an identifier to put in the container's registration metadata
// Pass it the instanceID for the docker host, and the the host port to lookup the associated ELB.
// useCache parameter, if true, will retrieve ELBv2 details from memory, rather than calling AWS.
// this is only really safe to use for heartbeat calls, as details can change dynamically
func GetELBV2ForContainer(containerID string, instanceID string, port int64, useCache bool) (lbinfo *LBInfo, err error) {

	// Retrieve from basic cache (for heartbeats)
	cacheKey := containerID
	if val, ok := lbCache[cacheKey]; ok && useCache {
		log.Println("Retrieving value from cache.")
		return val, nil
	}

	var lbArns []*string
	var lbPort *int64
	var tgArn string
	info := &LBInfo{}

	sess, err := session.NewSession()
	if err != nil {
		message := fmt.Errorf("Failed to create session connecting to AWS: %s", err)
		return nil, message
	}

	// Need to set the region here - we'll get it from instance metadata
	awsMetadata := GetMetadata()
	svc := elbv2.New(sess, awssdk.NewConfig().WithRegion(awsMetadata.Region))

	// TODO Note: There could be thousands of these, and we need to check them all.  Seems to be no
	// other way to retrieve a TG via instance/port with current API
	tgslice, err := getAllTargetGroups(svc)
	if err != nil || tgslice == nil {
		message := fmt.Errorf("Failed to retrieve Target Groups: %s", err)
		return nil, message
	}

	// Check each target group's target list for a matching port and instanceID
	// TODO Assumption: that that there is only one LB for the target group (though the data structure allows more)
	for _, tgs := range tgslice {
		for _, tg := range tgs.TargetGroups {

			thParams := &elbv2.DescribeTargetHealthInput{
				TargetGroupArn: awssdk.String(*tg.TargetGroupArn),
			}

			tarH, err := svc.DescribeTargetHealth(thParams)
			if err != nil {
				log.Printf("An error occurred using DescribeTargetHealth: %s \n", err.Error())
				return nil, err
			}

			for _, thd := range tarH.TargetHealthDescriptions {
				if *thd.Target.Port == port && *thd.Target.Id == instanceID {
					lbArns = tg.LoadBalancerArns
					tgArn = *tg.TargetGroupArn
					break
				}
			}
		}
		if lbArns != nil && tgArn != "" {
			break
		}
	}

	if err != nil || lbArns == nil {
		message := fmt.Errorf("failed to retrieve load balancer ARN")
		return nil, message
	}

	// Loop through the load balancer listeners to get the listener port for the target group
	lsnrParams := &elbv2.DescribeListenersInput{
		LoadBalancerArn: lbArns[0],
	}
	lnrData, err := svc.DescribeListeners(lsnrParams)
	if err != nil {
		log.Printf("An error occurred using DescribeListeners: %s \n", err.Error())
		return nil, err
	}
	for _, listener := range lnrData.Listeners {
		for _, act := range listener.DefaultActions {
			if *act.TargetGroupArn == tgArn {
				log.Printf("Found matching listener: %v", *listener.ListenerArn)
				lbPort = listener.Port
				break
			}
		}
	}
	if lbPort == nil {
		message := fmt.Errorf("error: Unable to identify listener port for ELBv2")
		return nil, message
	}

	// Get more information on the load balancer to retrieve the DNSName
	lbParams := &elbv2.DescribeLoadBalancersInput{
		LoadBalancerArns: lbArns,
	}
	lbData, err := svc.DescribeLoadBalancers(lbParams)
	if err != nil {
		log.Printf("An error occurred using DescribeLoadBalancers: %s \n", err.Error())
		return nil, err
	}
	log.Printf("LB Endpoint for Instance:%v Port:%v, Target Group:%v, is: %s:%s\n", instanceID, port, tgArn, *lbData.LoadBalancers[0].DNSName, strconv.FormatInt(*lbPort, 10))

	info.DNSName = *lbData.LoadBalancers[0].DNSName
	info.Port = *lbPort

	// Add to a basic cache for heartbeats
	lbCache[cacheKey] = info

	return info, nil
}

// CheckELBFlags - Helper function to check if the correct config flags are set to use ELBs
func CheckELBFlags(service *bridge.Service) bool {
	if service.Attrs["eureka_use_elbv2_endpoint"] != "" && service.Attrs["eureka_datacenterinfo_name"] != eureka.MyOwn {
		v, err := strconv.ParseBool(service.Attrs["eureka_use_elbv2_endpoint"])
		if err != nil {
			log.Printf("eureka: eureka_use_elbv2_endpoint must be valid boolean, was %v : %s", v, err)
			return false
		}
		return v
	}
	return false
}

// Helper function to create a registration struct, and change container registration
// useCache parameter is passed to getELBV2ForContainer
func setRegInfo(service *bridge.Service, registration *eureka.Instance, useCache bool) *eureka.Instance {

	awsMetadata := GetMetadata()

	elbMetadata, err := GetELBV2ForContainer(service.Origin.ContainerID, awsMetadata.InstanceID, int64(registration.Port), useCache)

	if err != nil {
		log.Printf("Unable to find associated ELBv2 for: %s, Error: %s\n", registration.HostName, err)
		return nil
	}

	elbStrPort := strconv.FormatInt(elbMetadata.Port, 10)
	elbEndpoint := elbMetadata.DNSName + "_" + elbStrPort

	registration.SetMetadataString("has-elbv2", "true")
	registration.SetMetadataString("elbv2-endpoint", elbEndpoint)

	elbReg := new(eureka.Instance)

	// Put a little metadata in here as required - setting InstanceID to the ELB endpoint prevents double registration
	elbReg.DataCenterInfo.Metadata = eureka.AmazonMetadataType{
		PublicHostname: elbMetadata.DNSName,
		HostName:       elbMetadata.DNSName,
		InstanceID:     elbEndpoint,
	}

	elbReg.Port = int(elbMetadata.Port)
	elbReg.IPAddr = elbMetadata.DNSName
	elbReg.App = service.Name
	elbReg.VipAddress = elbReg.IPAddr
	elbReg.HostName = elbMetadata.DNSName
	elbReg.DataCenterInfo.Name = eureka.Amazon
	elbReg.SetMetadataString("is-elbv2", "true")
	elbReg.Status = eureka.UP
	return elbReg
}

// RegisterELBv2 - If specified, also register an ELBv2 (application load balancer, ALB) endpoint in eureka, and alter service name
// for container registrations.  This will mean traffic is directed to the ALB rather than directly to containers
// though they are still registered in eureka for information purposes
func RegisterELBv2(service *bridge.Service, registration *eureka.Instance, client eureka.EurekaConnection) {
	if CheckELBFlags(service) {
		log.Printf("Found ELBv2 flags, will attempt to register LB for: %s\n", registration.HostName)
		elbReg := setRegInfo(service, registration, false)
		if elbReg != nil {
			err := client.RegisterInstance(elbReg)
			if err == nil {
				registrations[service.Origin.ContainerID] = true
			}
			// We reregister the container again too because ELB adds data to it
			client.ReregisterInstance(registration)
		}
	}
}

// DeregisterELBv2 - If specified, and all containers are gone, also deregister the ELBv2 (application load balancer, ALB) endpoint in eureka.
//
func DeregisterELBv2(service *bridge.Service, albEndpoint string, client eureka.EurekaConnection) {
	if CheckELBFlags(service) {

		if albEndpoint == "" {
			log.Printf("No endpoint available when attempting deregister.  ELBv2 will remain registered. Container: %v, IP: %v Port: %v", service.Origin.ContainerID, service.IP, service.Port)
			return
		}

		// Check if there are any containers around with this ALB still attached
		log.Printf("Found ELBv2 flags, will check if it needs to be deregistered too, for: %v\n", albEndpoint)
		appName := "CONTAINER_" + service.Name

		app, err := client.GetApp(appName)
		if err != nil {
			log.Printf("Unable to retrieve app metadata for %s: %s\n", appName, err)
			delete(registrations, service.Origin.ContainerID)
			delete(lbCache, service.Origin.ContainerID)
			return
		}

		if app != nil {
			for _, instance := range app.Instances {
				val, err := instance.Metadata.GetString("elbv2-endpoint")
				if err == nil && val == albEndpoint {
					log.Printf("Eureka entry still present for one or more ALB linked containers: %s\n", val)
					delete(registrations, service.Origin.ContainerID)
					delete(lbCache, service.Origin.ContainerID)
					return
				}
			}
		}

		if app == nil {
			log.Printf("Removing eureka entry for ELBv2: %s\n", albEndpoint)
			elbReg := new(eureka.Instance)
			elbReg.App = service.Name
			elbReg.HostName = albEndpoint // This uses the full endpoint identifier so eureka can find it to remove
			client.DeregisterInstance(elbReg)
			delete(registrations, service.Origin.ContainerID)
			delete(lbCache, service.Origin.ContainerID)
		}
	}
}

// HeartbeatELBv2 - Send a heartbeat to eureka for this ELBv2 registration.  Every host running registrator will send heartbeats, meaning they will
// be received more frequently than the --ttl-refresh interval if there are multiple hosts running registrator.
//
func HeartbeatELBv2(service *bridge.Service, registration *eureka.Instance, client eureka.EurekaConnection) {
	if CheckELBFlags(service) {
		log.Printf("Heartbeating ELBv2 for container: %s)\n", registration.HostName)

		if reg, ok := registrations[service.Origin.ContainerID]; !ok || !reg {
			log.Printf("ELBv2 failed previous registration.  Attempting register now.")
			RegisterELBv2(service, registration, client)
			// We reregister the container again too because ELB adds data to it
			client.ReregisterInstance(registration)
		}

		elbReg := setRegInfo(service, registration, true) // Can safely use cache when heartbeating
		if elbReg != nil {
			client.HeartBeatInstance(elbReg)
		}
	}
}
<|endoftext|>


<commit_before>package soko

import (
	"fmt"
	"net/url"
)

type Backend interface {
	// APIs to control backend metadata
	// Gets value from key
	Get(serverID string, key string) (string, error)

	// Put value on the key
	Put(serverID string, key string, value string) error

	// Delete value on the key
	Delete(serverID string, key string) error

	// TODO: implement
	// List(serverID string, prefix string) , returns all of values with serverID
	// Search(key string) , retruns serverID
	// Watch(serverID string, key string) , this blocks until change
	// ...
}

func FindBackend(config *Config) (Backend, error) {
	if config.URI == "" {
		// Defaults to return consul default backend
		return NewConsulBackend("", false)
	}

	u, err := url.Parse(config.URI)
	if err != nil {
		return nil, err
	}

	// TODO: implement AWS and OpenStack backend
	switch u.Scheme {
	case "consul":
		return NewConsulBackend(u.Host, false)
	case "consuls":
		return NewConsulBackend(u.Host, true)
	case "openstack":
		c, err := config.GetConfigBySection("openstack")
		if err != nil {
			return nil, err
		}
		return NewOpenStackBackend(c)
	default:
		return nil, fmt.Errorf("Unsupported schema: %s of %s", u.Scheme, config.URI)
	}
}
<commit_msg>Add backend entry to aws<commit_after>package soko

import (
	"fmt"
	"net/url"
)

type Backend interface {
	// APIs to control backend metadata
	// Gets value from key
	Get(serverID string, key string) (string, error)

	// Put value on the key
	Put(serverID string, key string, value string) error

	// Delete value on the key
	Delete(serverID string, key string) error

	// TODO: implement
	// List(serverID string, prefix string) , returns all of values with serverID
	// Search(key string) , retruns serverID
	// Watch(serverID string, key string) , this blocks until change
	// ...
}

func FindBackend(config *Config) (Backend, error) {
	if config.URI == "" {
		// Defaults to return consul default backend
		return NewConsulBackend("", false)
	}

	u, err := url.Parse(config.URI)
	if err != nil {
		return nil, err
	}

	// TODO: implement AWS and OpenStack backend
	switch u.Scheme {
	case "consul":
		return NewConsulBackend(u.Host, false)
	case "consuls":
		return NewConsulBackend(u.Host, true)
	case "openstack":
		c, err := config.GetConfigBySection("openstack")
		if err != nil {
			return nil, err
		}
		return NewOpenStackBackend(c)
	case "aws":
		c, err := config.GetConfigBySection("aws")
		if err != nil {
			return nil, err
		}
		return NewAWSBackend(c)
	default:
		return nil, fmt.Errorf("Unsupported schema: %s of %s", u.Scheme, config.URI)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package factom

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Balance struct {
	Balance int64
}

func ECBalance(key string) (*Balance, error) {
	resp, err := http.Get(
		fmt.Sprintf("http://%s/v1/entry-credit-balance/%s", server, key))
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	b := new(Balance)
	if err := json.Unmarshal(body, b); err != nil {
		return nil, err
	}

	return b, nil
}

func FctBalance(key string) (*Balance, error) {

    str := fmt.Sprintf("http://%s/v1/factoid-balance/%s", serverFct, key)
    resp, err := http.Get(str)
    if err != nil {
        return nil, err
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }
    resp.Body.Close()

    b := new(Balance)
    if err := json.Unmarshal(body, b); err != nil {
        return nil, err
    }

    return b, nil
}

func GenerateFactoidAddress(name string) (string, error) {

    type address struct {
        Address string
    }

    str := fmt.Sprintf("http://%s/v1/factoid-generate-address/%s", serverFct, name)
    resp, err := http.Get(str)
    if err != nil {
        return "", err
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return "", err
    }
    resp.Body.Close()

    b := new(address)
    if err := json.Unmarshal(body, b); err != nil || len(b.Address)==0  {
        return "", fmt.Errorf("Duplicate Address ")
    }

    return b.Address, nil
}<commit_msg>Added Factoid support for transactions<commit_after>// Copyright 2015 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package factom

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Balance struct {
	Balance int64
}

func ECBalance(key string) (*Balance, error) {
	resp, err := http.Get(
		fmt.Sprintf("http://%s/v1/entry-credit-balance/%s", server, key))
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	b := new(Balance)
	if err := json.Unmarshal(body, b); err != nil {
		return nil, err
	}

	return b, nil
}

func FctBalance(key string) (*Balance, error) {
    str := fmt.Sprintf("http://%s/v1/factoid-balance/%s", serverFct, key)
    resp, err := http.Get(str)
    if err != nil {
        return nil, err
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }
    resp.Body.Close()

    b := new(Balance)
    if err := json.Unmarshal(body, b); err != nil {
        return nil, err
    }

    return b, nil
}

func GenerateFactoidAddress(name string) (string, error) {

    type address struct {
        Address string
    }

    str := fmt.Sprintf("http://%s/v1/factoid-generate-address/%s", serverFct, name)
    resp, err := http.Get(str)
    if err != nil {
        return "", err
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return "", err
    }
    resp.Body.Close()

    b := new(address)
    if err := json.Unmarshal(body, b); err != nil || len(b.Address)==0  {
        return "", fmt.Errorf("Duplicate Address ")
    }

    return b.Address, nil
}<|endoftext|>


<commit_before>package bank

import (
	"errors"

	"github.com/PMoneda/gonnie"

	"bitbucket.org/mundipagg/boletoapi/config"
	"bitbucket.org/mundipagg/boletoapi/letters"
	"bitbucket.org/mundipagg/boletoapi/log"
	"bitbucket.org/mundipagg/boletoapi/models"
	"bitbucket.org/mundipagg/boletoapi/tmpl"
)

type bankBB struct {
	validate *models.Validator
	log      *log.Log
}

//Cria uma nova instância do objeto que implementa os serviços do Banco do Brasil e configura os validadores que serão utilizados
func newBB() bankBB {
	b := bankBB{
		validate: models.NewValidator(),
		log:      log.CreateLog(),
	}
	b.validate.Push(bbValidateAccountAndDigit)
	b.validate.Push(bbValidateAgencyAndDigit)
	b.validate.Push(bbValidateOurNumber)
	b.validate.Push(bbValidateWalletVariation)
	b.validate.Push(baseValidateAmountInCents)
	b.validate.Push(baseValidateExpireDate)
	b.validate.Push(baseValidateBuyerDocumentNumber)
	b.validate.Push(baseValidateRecipientDocumentNumber)
	b.validate.Push(bbValidateTitleInstructions)
	b.validate.Push(bbValidateTitleDocumentNumber)
	return b
}

//Log retorna a referencia do log
func (b bankBB) Log() *log.Log {
	return b.log
}

func (b *bankBB) login(boleto *models.BoletoRequest) (string, error) {
	type errorAuth struct {
		Error            string `json:"error"`
		ErrorDescription string `json:"error_description"`
	}
	r := gonnie.NewPipe()
	url := config.Get().URLBBToken
	from, resp := letters.GetBBAuthLetters()
	bod := r.From("message://?source=inline", boleto, from, tmpl.GetFuncMaps())
	r = r.To("logseq://?type=request&url="+url, b.log)
	bod = bod.To(url, map[string]string{"method": "POST", "insecureSkipVerify": "true"})
	r = r.To("logseq://?type=response&url="+url, b.log)
	ch := bod.Choice().When(gonnie.Header("status").IsEqualTo("200")).To("transform://?format=json", resp, `{{.authToken}}`)
	ch = ch.Otherwise().To("unmarshall://?format=json", new(errorAuth))
	result := bod.GetBody()
	switch t := result.(type) {
	case string:
		return t, nil
	case *errorAuth:
		return "", errors.New(t.ErrorDescription)
	}
	return "", errors.New("Saída inválida")
}

//ProcessBoleto faz o processamento de registro de boleto
func (b bankBB) ProcessBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) {
	errs := b.ValidateBoleto(boleto)
	if len(errs) > 0 {
		return models.BoletoResponse{Errors: errs}, nil
	}
	tok, err := b.login(boleto)
	if err != nil {
		return models.BoletoResponse{}, err
	}
	boleto.Authentication.AuthorizationToken = tok
	return b.RegisterBoleto(boleto)
}

func (b bankBB) RegisterBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) {
	r := gonnie.NewPipe()
	url := config.Get().URLBBRegisterBoleto
	from := letters.GetRegisterBoletoBBTmpl()
	r = r.From("message://?source=inline", boleto, from, tmpl.GetFuncMaps())
	r = r.To("logseq://?type=request&url="+url, b.log)
	r = r.To(url, map[string]string{"method": "POST", "insecureSkipVerify": "true"})
	r = r.To("logseq://?type=response&url="+url, b.log)
	r = r.To("transform://?format=xml", letters.GetBBregisterLetter(), letters.GetRegisterBoletoAPIResponseTmpl(), tmpl.GetFuncMaps())
	r = r.To("unmarshall://?format=json", new(models.BoletoResponse))
	body := r.GetBody().(*models.BoletoResponse)
	return *body, nil
}

func (b bankBB) ValidateBoleto(boleto *models.BoletoRequest) models.Errors {
	return models.Errors(b.validate.Assert(boleto))
}

//GetBankNumber retorna o codigo do banco
func (b bankBB) GetBankNumber() models.BankNumber {
	return models.BancoDoBrasil
}
<commit_msg>:art: adiciona um tratamento para caso a mensagem do banco não venha status 200<commit_after>package bank

import (
	"errors"

	"github.com/PMoneda/gonnie"

	"bitbucket.org/mundipagg/boletoapi/config"
	"bitbucket.org/mundipagg/boletoapi/letters"
	"bitbucket.org/mundipagg/boletoapi/log"
	"bitbucket.org/mundipagg/boletoapi/models"
	"bitbucket.org/mundipagg/boletoapi/tmpl"
)

type bankBB struct {
	validate *models.Validator
	log      *log.Log
}

//Cria uma nova instância do objeto que implementa os serviços do Banco do Brasil e configura os validadores que serão utilizados
func newBB() bankBB {
	b := bankBB{
		validate: models.NewValidator(),
		log:      log.CreateLog(),
	}
	b.validate.Push(bbValidateAccountAndDigit)
	b.validate.Push(bbValidateAgencyAndDigit)
	b.validate.Push(bbValidateOurNumber)
	b.validate.Push(bbValidateWalletVariation)
	b.validate.Push(baseValidateAmountInCents)
	b.validate.Push(baseValidateExpireDate)
	b.validate.Push(baseValidateBuyerDocumentNumber)
	b.validate.Push(baseValidateRecipientDocumentNumber)
	b.validate.Push(bbValidateTitleInstructions)
	b.validate.Push(bbValidateTitleDocumentNumber)
	return b
}

//Log retorna a referencia do log
func (b bankBB) Log() *log.Log {
	return b.log
}

func (b *bankBB) login(boleto *models.BoletoRequest) (string, error) {
	type errorAuth struct {
		Error            string `json:"error"`
		ErrorDescription string `json:"error_description"`
	}
	r := gonnie.NewPipe()
	url := config.Get().URLBBToken
	from, resp := letters.GetBBAuthLetters()
	bod := r.From("message://?source=inline", boleto, from, tmpl.GetFuncMaps())
	r = r.To("logseq://?type=request&url="+url, b.log)
	bod = bod.To(url, map[string]string{"method": "POST", "insecureSkipVerify": "true"})
	r = r.To("logseq://?type=response&url="+url, b.log)
	ch := bod.Choice().When(gonnie.Header("status").IsEqualTo("200")).To("transform://?format=json", resp, `{{.authToken}}`)
	ch = ch.Otherwise().To("unmarshall://?format=json", new(errorAuth))
	result := bod.GetBody()
	switch t := result.(type) {
	case string:
		return t, nil
	case *errorAuth:
		return "", errors.New(t.ErrorDescription)
	}
	return "", errors.New("Saída inválida")
}

//ProcessBoleto faz o processamento de registro de boleto
func (b bankBB) ProcessBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) {
	errs := b.ValidateBoleto(boleto)
	if len(errs) > 0 {
		return models.BoletoResponse{Errors: errs}, nil
	}
	tok, err := b.login(boleto)
	if err != nil {
		return models.BoletoResponse{}, err
	}
	boleto.Authentication.AuthorizationToken = tok
	return b.RegisterBoleto(boleto)
}

func (b bankBB) RegisterBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) {
	r := gonnie.NewPipe()
	url := config.Get().URLBBRegisterBoleto
	from := letters.GetRegisterBoletoBBTmpl()
	r = r.From("message://?source=inline", boleto, from, tmpl.GetFuncMaps())
	r = r.To("logseq://?type=request&url="+url, b.log)
	r = r.To(url, map[string]string{"method": "POST", "insecureSkipVerify": "true"})
	r = r.To("logseq://?type=response&url="+url, b.log)
	ch := r.Choice()
	ch = ch.When(gonnie.Header("status").IsEqualTo("200"))
	ch = ch.To("transform://?format=xml", letters.GetBBregisterLetter(), letters.GetRegisterBoletoAPIResponseTmpl(), tmpl.GetFuncMaps())
	ch = ch.To("unmarshall://?format=json", new(models.BoletoResponse))
	ch = ch.Otherwise()
	ch = ch.To("logseq://?type=response&url="+url, b.log).To("apierro://")
	body := r.GetBody().(*models.BoletoResponse)
	return *body, nil
}

func (b bankBB) ValidateBoleto(boleto *models.BoletoRequest) models.Errors {
	return models.Errors(b.validate.Assert(boleto))
}

//GetBankNumber retorna o codigo do banco
func (b bankBB) GetBankNumber() models.BankNumber {
	return models.BancoDoBrasil
}
<|endoftext|>


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

package bar

import (
	"bufio"
	"encoding/json"
	"io"
	"os"
	"reflect"
	"strconv"
	"strings"
)

// identifier stores a Name and instance pair used when communicating with i3bar.
// This information is never exposed outside the bar.
type identifier struct {
	Name     string `json:"name"`
	Instance string `json:"instance"`
}

// i3Output is sent to i3bar. It wraps the bar output and adds name and instance.
type i3Output struct {
	*Output
	identifier
}

// i3Event instances are received from i3bar on stdin.
type i3Event struct {
	Event
	identifier
}

// i3Header is sent at the beginning of output.
type i3Header struct {
	Version     int  `json:"version"`
	StopSignal  int  `json:"stop_signal,omitempty"`
	ContSignal  int  `json:"cont_signal,omitempty"`
	ClickEvents bool `json:"click_events"`
}

// i3Module wraps Module with extra information to help run i3bar.
type i3Module struct {
	Module
	Identifier identifier
	LastOutput i3Output
}

// output converts the module's output to i3output by adding the name and instance.
func (m i3Module) output(ch chan<- i3Output) {
	for o := range m.Stream() {
		ch <- i3Output{Output: o, identifier: m.Identifier}
	}
}

// I3Bar is a "bar" instance that handles events and streams output.
type I3Bar struct {
	// The list of modules that make up this bar.
	i3Modules []i3Module
	// The channel that aggregates all the module outputs.
	outputs chan i3Output
	// The channel that aggregates all events from i3.
	events chan i3Event
	// The Reader to read events from (e.g. stdin)
	reader io.Reader
	// The Writer to write bar output to (e.g. stdout)
	writer io.Writer
	// A json encoder set to write to the output stream.
	encoder *json.Encoder
}

// Add adds a module to a bar, and returns the bar for chaining.
func (b *I3Bar) Add(modules ...Module) *I3Bar {
	for _, m := range modules {
		b.addModule(m)
	}
	// Return the bar for chaining (e.g. bar.Add(x, y).Run())
	return b
}

// addModule adds a single module to the bar.
func (b *I3Bar) addModule(module Module) {
	// Use the type, e.g. "*tztime.module", as the name. This doesn't matter,
	// but i3 would like to have it.
	name := reflect.TypeOf(module).String()
	// Use the position of the module in the list as the "instance", so when i3bar
	// sends us events, we can use atoi(instance) to get the correct module.
	instance := strconv.Itoa(len(b.i3Modules))
	i3Module := i3Module{
		Module:     module,
		Identifier: identifier{name, instance},
	}
	b.i3Modules = append(b.i3Modules, i3Module)
}

// Run sets up all the streams and enters the main loop.
func (b *I3Bar) Run() error {

	// Write header.
	header := i3Header{
		Version:     1,
		ClickEvents: true,
	}
	// Set up the encoder for the output stream,
	// so that module outputs can be written directly.
	b.encoder = json.NewEncoder(b.writer)
	if err := b.encoder.Encode(&header); err != nil {
		return err
	}
	// Start the infinite array.
	if _, err := io.WriteString(b.writer, "["); err != nil {
		return err
	}

	// Read events from the input stream, pipe them to the events channel.
	go b.readEvents()
	// Merge all module outputs to the outputs channel.
	for _, m := range b.i3Modules {
		go m.output(b.outputs)
	}

	// Infinite arrays on both sides.
	for {
		select {
		case out := <-b.outputs:
			// Module outputs update it's own "last output"
			// but the complete bar needs to printed each time.
			if module, ok := b.get(out.Instance); ok {
				module.LastOutput = out
				if err := b.print(); err != nil {
					return err
				}
			}
		case event := <-b.events:
			// Events are stripped of name and instance before being dispatched to the
			// correct module.
			if module, ok := b.get(event.Instance); ok {
				// Goroutine to prevent click handlers from blocking the bar.
				go module.Click(event.Event)
			}
		}
	}
}

// Run with a list of modules just adds each module and runs the bar on stdout/stdin.
func Run(modules ...Module) error {
	return New().Add(modules...).Run()
}

// RunOnIo takes a list of modules and the input/output streams, and runs the bar.
func RunOnIo(reader io.Reader, writer io.Writer, modules ...Module) error {
	return NewOnIo(reader, writer).Add(modules...).Run()
}

// New constructs a new bar running on standard I/O.
func New() *I3Bar {
	return NewOnIo(os.Stdin, os.Stdout)
}

// NewOnIo constructs a new bar with an input and output stream, for maximum flexibility.
func NewOnIo(reader io.Reader, writer io.Writer) *I3Bar {
	return &I3Bar{
		outputs: make(chan i3Output),
		events:  make(chan i3Event),
		reader:  reader,
		writer:  writer,
	}
}

// print outputs the entire bar, using the last output for each module.
func (b *I3Bar) print() error {
	// i3bar requires the entire bar to be printed at once, so we just take the
	// last cached value for each module and construct the current bar.
	// The bar will update any modules before calling this method, so the
	// LastOutput property of each module will represent the current state.
	var outputs []i3Output
	for _, m := range b.i3Modules {
		out := m.LastOutput
		if out.Output == nil {
			continue
		}
		outputs = append(outputs, out)
	}
	if err := b.encoder.Encode(outputs); err != nil {
		return err
	}
	_, err := io.WriteString(b.writer, ",\n")
	return err
}

// get finds the module that corresponds to the given "instance" from i3.
func (b *I3Bar) get(instance string) (*i3Module, bool) {
	index, err := strconv.Atoi(instance)
	if err != nil {
		return nil, false
	}
	if index < 0 || len(b.i3Modules) <= index {
		return nil, false
	}
	return &b.i3Modules[index], true
}

// readEvents parses the infinite stream of events received from i3.
func (b *I3Bar) readEvents() {
	// Buffered I/O to allow complete events to be read in at once.
	reader := bufio.NewReader(b.reader)
	// Consume opening '['
	if rune, _, err := reader.ReadRune(); err != nil || rune != '[' {
		return
	}
	var event i3Event
	for {
		// While the 'proper' way to implement this infinite parser would be to keep
		// a state machine and hook into json parsing and stuff, we'll take a
		// shortcut since we know there are no nested objects. So all we have to do
		// is read until the first '}', decode it, consume the ',', and repeat.
		eventJSON, err := reader.ReadString('}')
		if err != nil {
			return
		}
		// The '}' is consumed by ReadString, but required by json Decoder.
		decoder := json.NewDecoder(strings.NewReader(eventJSON + "}"))
		err = decoder.Decode(&event)
		if err != nil {
			return
		}
		b.events <- event
		// Consume ','
		if _, err := reader.ReadString(','); err != nil {
			return
		}
	}
}
<commit_msg>Panic if a module is added to an already running bar<commit_after>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bar

import (
	"bufio"
	"encoding/json"
	"io"
	"os"
	"reflect"
	"strconv"
	"strings"
)

// identifier stores a Name and instance pair used when communicating with i3bar.
// This information is never exposed outside the bar.
type identifier struct {
	Name     string `json:"name"`
	Instance string `json:"instance"`
}

// i3Output is sent to i3bar. It wraps the bar output and adds name and instance.
type i3Output struct {
	*Output
	identifier
}

// i3Event instances are received from i3bar on stdin.
type i3Event struct {
	Event
	identifier
}

// i3Header is sent at the beginning of output.
type i3Header struct {
	Version     int  `json:"version"`
	StopSignal  int  `json:"stop_signal,omitempty"`
	ContSignal  int  `json:"cont_signal,omitempty"`
	ClickEvents bool `json:"click_events"`
}

// i3Module wraps Module with extra information to help run i3bar.
type i3Module struct {
	Module
	Identifier identifier
	LastOutput i3Output
}

// output converts the module's output to i3output by adding the name and instance.
func (m i3Module) output(ch chan<- i3Output) {
	for o := range m.Stream() {
		ch <- i3Output{Output: o, identifier: m.Identifier}
	}
}

// I3Bar is a "bar" instance that handles events and streams output.
type I3Bar struct {
	// The list of modules that make up this bar.
	i3Modules []i3Module
	// The channel that aggregates all the module outputs.
	outputs chan i3Output
	// The channel that aggregates all events from i3.
	events chan i3Event
	// The Reader to read events from (e.g. stdin)
	reader io.Reader
	// The Writer to write bar output to (e.g. stdout)
	writer io.Writer
	// A json encoder set to write to the output stream.
	encoder *json.Encoder
	// Flipped when Run() is called, to prevent issues with modules
	// being added after the bar has been started.
	started bool
}

// Add adds a module to a bar, and returns the bar for chaining.
func (b *I3Bar) Add(modules ...Module) *I3Bar {
	for _, m := range modules {
		b.addModule(m)
	}
	// Return the bar for chaining (e.g. bar.Add(x, y).Run())
	return b
}

// addModule adds a single module to the bar.
func (b *I3Bar) addModule(module Module) {
	// Panic if adding modules to an already running bar.
	// TODO: Support this in the future.
	if b.started {
		panic("Cannot add modules after .Run()")
	}
	// Use the type, e.g. "*tztime.module", as the name. This doesn't matter,
	// but i3 would like to have it.
	name := reflect.TypeOf(module).String()
	// Use the position of the module in the list as the "instance", so when i3bar
	// sends us events, we can use atoi(instance) to get the correct module.
	instance := strconv.Itoa(len(b.i3Modules))
	i3Module := i3Module{
		Module:     module,
		Identifier: identifier{name, instance},
	}
	b.i3Modules = append(b.i3Modules, i3Module)
}

// Run sets up all the streams and enters the main loop.
func (b *I3Bar) Run() error {

	// Write header.
	header := i3Header{
		Version:     1,
		ClickEvents: true,
	}
	// Set up the encoder for the output stream,
	// so that module outputs can be written directly.
	b.encoder = json.NewEncoder(b.writer)
	if err := b.encoder.Encode(&header); err != nil {
		return err
	}
	// Start the infinite array.
	if _, err := io.WriteString(b.writer, "["); err != nil {
		return err
	}

	// Mark the bar as started.
	b.started = true

	// Read events from the input stream, pipe them to the events channel.
	go b.readEvents()
	// Merge all module outputs to the outputs channel.
	for _, m := range b.i3Modules {
		go m.output(b.outputs)
	}

	// Infinite arrays on both sides.
	for {
		select {
		case out := <-b.outputs:
			// Any output from a module updates its "last output",
			// but the complete bar needs to printed each time.
			if module, ok := b.get(out.Instance); ok {
				module.LastOutput = out
				if err := b.print(); err != nil {
					return err
				}
			}
		case event := <-b.events:
			// Events are stripped of name and instance before being dispatched to the
			// correct module.
			if module, ok := b.get(event.Instance); ok {
				// Goroutine to prevent click handlers from blocking the bar.
				go module.Click(event.Event)
			}
		}
	}
}

// Run with a list of modules just adds each module and runs the bar on stdout/stdin.
func Run(modules ...Module) error {
	return New().Add(modules...).Run()
}

// RunOnIo takes a list of modules and the input/output streams, and runs the bar.
func RunOnIo(reader io.Reader, writer io.Writer, modules ...Module) error {
	return NewOnIo(reader, writer).Add(modules...).Run()
}

// New constructs a new bar running on standard I/O.
func New() *I3Bar {
	return NewOnIo(os.Stdin, os.Stdout)
}

// NewOnIo constructs a new bar with an input and output stream, for maximum flexibility.
func NewOnIo(reader io.Reader, writer io.Writer) *I3Bar {
	return &I3Bar{
		outputs: make(chan i3Output),
		events:  make(chan i3Event),
		reader:  reader,
		writer:  writer,
	}
}

// print outputs the entire bar, using the last output for each module.
func (b *I3Bar) print() error {
	// i3bar requires the entire bar to be printed at once, so we just take the
	// last cached value for each module and construct the current bar.
	// The bar will update any modules before calling this method, so the
	// LastOutput property of each module will represent the current state.
	var outputs []i3Output
	for _, m := range b.i3Modules {
		out := m.LastOutput
		if out.Output == nil {
			continue
		}
		outputs = append(outputs, out)
	}
	if err := b.encoder.Encode(outputs); err != nil {
		return err
	}
	_, err := io.WriteString(b.writer, ",\n")
	return err
}

// get finds the module that corresponds to the given "instance" from i3.
func (b *I3Bar) get(instance string) (*i3Module, bool) {
	index, err := strconv.Atoi(instance)
	if err != nil {
		return nil, false
	}
	if index < 0 || len(b.i3Modules) <= index {
		return nil, false
	}
	return &b.i3Modules[index], true
}

// readEvents parses the infinite stream of events received from i3.
func (b *I3Bar) readEvents() {
	// Buffered I/O to allow complete events to be read in at once.
	reader := bufio.NewReader(b.reader)
	// Consume opening '['
	if rune, _, err := reader.ReadRune(); err != nil || rune != '[' {
		return
	}
	var event i3Event
	for {
		// While the 'proper' way to implement this infinite parser would be to keep
		// a state machine and hook into json parsing and stuff, we'll take a
		// shortcut since we know there are no nested objects. So all we have to do
		// is read until the first '}', decode it, consume the ',', and repeat.
		eventJSON, err := reader.ReadString('}')
		if err != nil {
			return
		}
		// The '}' is consumed by ReadString, but required by json Decoder.
		decoder := json.NewDecoder(strings.NewReader(eventJSON + "}"))
		err = decoder.Decode(&event)
		if err != nil {
			return
		}
		b.events <- event
		// Consume ','
		if _, err := reader.ReadString(','); err != nil {
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"html/template"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"time"

	"golang.org/x/net/websocket"
)

type server struct {
	ListenAddr string
	Tick       time.Duration
}

func Serve(opts ...func(*server)) {
	var s server
	defaults(&s)

	for _, fn := range opts {
		fn(&s)
	}
	ln, err := net.Listen("tcp", s.ListenAddr)
	if err != nil {
		log.Fatalf("memstat: %s", err)
	}
	s.ListenAddr = ln.Addr().String()

	mux := http.NewServeMux()
	mux.Handle("/", s)
	mux.Handle("/scripts/", http.FileServer(http.Dir("web")))
	mux.Handle("/memstats-feed", websocket.Handler(s.ServeSocket))
	if err = http.Serve(ln, mux); err != nil {
		log.Fatalf("memstat: %s", err)
	}
}

func (s server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	t, err := template.ParseFiles("web/viewer.html")
	if err != nil {
		fmt.Fprintf(w, "Error parsing template: %s", err)
		return
	}
	if err := t.Execute(w, s); err != nil {
		fmt.Fprintf(w, "Error parsing template: %s", err)
	}
}

func (s server) ServeSocket(ws *websocket.Conn) {
	payload := struct {
		Stats runtime.MemStats
		CPU   string
	}{}
	var buf bytes.Buffer
	pprof.StartCPUProfile(&buf)
	for {
		runtime.ReadMemStats(&payload.Stats)
		payload.CPU = buf.String()
		buf.Reset()
		websocket.JSON.Send(ws, payload)
		<-time.After(s.Tick)
	}
	pprof.StopCPUProfile()
}

func main() {
	go Serve()
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt)
	<-sig
}
<commit_msg>Forgot defaults<commit_after>package main

import (
	"bytes"
	"fmt"
	"html/template"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"time"

	"golang.org/x/net/websocket"
)

type server struct {
	ListenAddr string
	Tick       time.Duration
}

func Serve(opts ...func(*server)) {
	var s server
	defaults(&s)

	for _, fn := range opts {
		fn(&s)
	}
	ln, err := net.Listen("tcp", s.ListenAddr)
	if err != nil {
		log.Fatalf("memstat: %s", err)
	}
	s.ListenAddr = ln.Addr().String()

	mux := http.NewServeMux()
	mux.Handle("/", s)
	mux.Handle("/scripts/", http.FileServer(http.Dir("web")))
	mux.Handle("/memstats-feed", websocket.Handler(s.ServeSocket))
	if err = http.Serve(ln, mux); err != nil {
		log.Fatalf("memstat: %s", err)
	}
}

func (s server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	t, err := template.ParseFiles("web/viewer.html")
	if err != nil {
		fmt.Fprintf(w, "Error parsing template: %s", err)
		return
	}
	if err := t.Execute(w, s); err != nil {
		fmt.Fprintf(w, "Error parsing template: %s", err)
	}
}

func (s server) ServeSocket(ws *websocket.Conn) {
	payload := struct {
		Stats runtime.MemStats
		CPU   string
	}{}
	var buf bytes.Buffer
	pprof.StartCPUProfile(&buf)
	for {
		runtime.ReadMemStats(&payload.Stats)
		payload.CPU = buf.String()
		buf.Reset()
		websocket.JSON.Send(ws, payload)
		<-time.After(s.Tick)
	}
	pprof.StopCPUProfile()
}

func defaults(s *server) {
	s.ListenAddr = ":6061"
	s.Tick = 2 * time.Second
}

func main() {
	go Serve()

	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt)
	<-sig
}
<|endoftext|>