<commit_before>package kv

import (
	"encoding/json"
	"fmt"
	"kasper/metrics"
	"strings"

	"log"

	"golang.org/x/net/context"
	elastic "gopkg.in/olivere/elastic.v5"

	"github.com/movio/kasper/util"
)

type ElasticsearchOpts struct {
	GetIndexSettings func(indexName string) string
	GetIndexMappings func(indexName string) string
}

var DefaultElasticsearchOpts ElasticsearchOpts = ElasticsearchOpts{
	GetIndexSettings: func(indexName string) string {
		return `{
			"index.translog.durability": "request"
		}`
	},
	GetIndexMappings: func(indexName string) string {
		return `{
			"_all" : {
				"enabled" : false
			},
			"dynamic_templates": [{
				"no_index": {
					"mapping": {
						"index": "no"
					},
					"match": "*"
				}
			}]
		}`
	},
}

type indexAndType struct {
	indexName string
	indexType string
}

// ElasticsearchKeyValueStore is a key-value storage that uses ElasticSearch.
// In this key-value store, all keys must have the format "<index>/<type>/<_id>".
type ElasticsearchKeyValueStore struct {
	elasticSearchOpts *ElasticsearchOpts
	witness           *util.StructPtrWitness
	client            *elastic.Client
	context           context.Context
	existingIndexes   []indexAndType
	metricsProvider   metrics.Provider
	getCounter        metrics.Counter
	getAllSummary     metrics.Summary
	putCounter        metrics.Counter
	putAllSummary     metrics.Summary
	deleteCounter     metrics.Counter
	flushCounter      metrics.Counter
}

// NewESKeyValueStore creates new ElasticsearchKeyValueStore instance.
// Host must of the format hostname:port.
// StructPtr should be a pointer to struct type that is used.
// for serialization and deserialization of store values.
func NewESKeyValueStore(url string, structPtr interface{}) *ElasticsearchKeyValueStore {
	return NewESKeyValueStoreWithOpts(url, structPtr, &DefaultElasticsearchOpts, &metrics.NoopMetricsProvider{})
}

// TBD
func NewESKeyValueStoreWithOpts(url string, structPtr interface{}, opts *ElasticsearchOpts, metricsProvider metrics.Provider) *ElasticsearchKeyValueStore {
	client, err := elastic.NewClient(
		elastic.SetURL(url),
		elastic.SetSniff(false), // FIXME: workaround for issues with ES in docker
	)
	if err != nil {
		panic(fmt.Sprintf("Cannot create ElasticSearch Client to '%s': %s", url, err))
	}
	return &ElasticsearchKeyValueStore{
		elasticSearchOpts: opts,
		witness:           util.NewStructPtrWitness(structPtr),
		client:            client,
		context:           context.Background(),
		existingIndexes:   nil,
		metricsProvider:   metricsProvider,
	}
}

func (s *ElasticsearchKeyValueStore) createMetrics() {
	s.getCounter = s.metricsProvider.NewCounter("ElasticsearchKeyValueStore_Get", "Number of Get() calls")
	s.getAllSummary = s.metricsProvider.NewSummary("ElasticsearchKeyValueStore_GetAll", "Summary of GetAll() calls")
	s.putCounter = s.metricsProvider.NewCounter("ElasticsearchKeyValueStore_Put", "Number of Put() calls")
	s.putAllSummary = s.metricsProvider.NewSummary("ElasticsearchKeyValueStore_PutAll", "Summary of PutAll() calls")
	s.deleteCounter = s.metricsProvider.NewCounter("ElasticsearchKeyValueStore_Delete", "Number of Delete() calls")
	s.flushCounter = s.metricsProvider.NewCounter("ElasticsearchKeyValueStore_Flush", "Summary of Flush() calls")
}

func (s *ElasticsearchKeyValueStore) checkOrCreateIndex(indexName string, indexType string) {
	for _, existing := range s.existingIndexes {
		if existing.indexName == indexName && existing.indexType == indexType {
			return
		}
	}
	exists, err := s.client.IndexExists(indexName).Do(s.context)
	if err != nil {
		panic(fmt.Sprintf("Failed to check if index exists: %s", err))
	}
	if !exists {
		_, err = s.client.
			CreateIndex(indexName).
			BodyString(s.elasticSearchOpts.GetIndexSettings(indexName)).
			Do(s.context)
		if err != nil {
			panic(fmt.Sprintf("Failed to create index: %s", err))
		}
		s.putMapping(indexName, indexType)
	}

	s.existingIndexes = append(s.existingIndexes, indexAndType{indexName, indexType})
}

func (s *ElasticsearchKeyValueStore) putMapping(indexName string, indexType string) {
	resp, err := s.client.
		PutMapping().
		Index(indexName).
		Type(indexType).
		BodyString(s.elasticSearchOpts.GetIndexMappings(indexName)).
		Do(s.context)
	if err != nil {
		panic(fmt.Sprintf("Failed to put mapping for index: %s/%s: %s", indexName, indexType, err))
	}
	if resp == nil {
		panic(fmt.Sprintf("Expected put mapping response; got: %v", resp))
	}
	if !resp.Acknowledged {
		panic(fmt.Sprintf("Expected put mapping ack; got: %v", resp.Acknowledged))
	}
}

// Get gets value by key from store
func (s *ElasticsearchKeyValueStore) Get(key string) (interface{}, error) {
	s.getCounter.Inc()
	keyParts := strings.Split(key, "/")
	if len(keyParts) != 3 {
		return nil, fmt.Errorf("invalid key: '%s'", key)
	}
	indexName := keyParts[0]
	indexType := keyParts[1]
	valueID := keyParts[2]

	s.checkOrCreateIndex(indexName, indexType)

	rawValue, err := s.client.Get().
		Index(indexName).
		Type(indexType).
		Id(valueID).
		Do(s.context)

	if fmt.Sprintf("%s", err) == "elastic: Error 404 (Not Found)" {
		return s.witness.Nil(), nil
	}

	if err != nil {
		return s.witness.Nil(), err
	}

	if !rawValue.Found {
		return s.witness.Nil(), nil
	}

	structPtr := s.witness.Allocate()
	err = json.Unmarshal(*rawValue.Source, structPtr)
	if err != nil {
		return s.witness.Nil(), err
	}
	return structPtr, nil
}

// TBD
func (s *ElasticsearchKeyValueStore) GetAll(keys []string) ([]*Entry, error) {
	s.getAllSummary.Observe(float64(len(keys)))
	multiGet := s.client.MultiGet()
	for _, key := range keys {
		keyParts := strings.Split(key, "/")
		if len(keyParts) != 3 {
			return nil, fmt.Errorf("invalid key: '%s'", key)
		}
		indexName := keyParts[0]
		indexType := keyParts[1]
		valueID := keyParts[2]

		s.checkOrCreateIndex(indexName, indexType)

		item := elastic.NewMultiGetItem().
			Index(indexName).
			Type(indexType).
			Id(valueID)

		multiGet.Add(item)
	}
	response, err := multiGet.Do(s.context)
	if err != nil {
		return nil, err
	}
	entries := make([]*Entry, len(keys))
	for i, doc := range response.Docs {
		var structPtr interface{}
		if !doc.Found {
			structPtr = s.witness.Nil()
		} else {
			structPtr = s.witness.Allocate()
			err = json.Unmarshal(*doc.Source, structPtr)
			if err != nil {
				return nil, err
			}
		}
		entries[i] = &Entry{keys[i], structPtr}
	}
	return entries, nil
}

// Put updates key in store with serialized value
func (s *ElasticsearchKeyValueStore) Put(key string, structPtr interface{}) error {
	s.witness.Assert(structPtr)
	s.putCounter.Inc()
	keyParts := strings.Split(key, "/")
	if len(keyParts) != 3 {
		return fmt.Errorf("invalid key: '%s'", key)
	}
	indexName := keyParts[0]
	indexType := keyParts[1]
	valueID := keyParts[2]

	s.checkOrCreateIndex(indexName, indexType)

	_, err := s.client.Index().
		Index(indexName).
		Type(indexType).
		Id(valueID).
		BodyJson(structPtr).
		Do(s.context)

	return err
}

// PutAll bulk executes Put operation for several entries
func (s *ElasticsearchKeyValueStore) PutAll(entries []*Entry) error {
	s.putAllSummary.Observe(float64(len(entries)))
	if len(entries) == 0 {
		return nil
	}
	bulk := s.client.Bulk()
	for _, entry := range entries {
		keyParts := strings.Split(entry.Key, "/")
		if len(keyParts) != 3 {
			return fmt.Errorf("invalid key: '%s'", entry.Key)
		}
		indexName := keyParts[0]
		indexType := keyParts[1]
		valueID := keyParts[2]

		s.witness.Assert(entry.Value)
		s.checkOrCreateIndex(indexName, indexType)

		bulk.Add(elastic.NewBulkIndexRequest().
			Index(indexName).
			Type(indexType).
			Id(valueID).
			Doc(entry.Value),
		)
	}
	_, err := bulk.Do(s.context)
	return err
}

// Delete removes key from store
func (s *ElasticsearchKeyValueStore) Delete(key string) error {
	s.deleteCounter.Inc()
	keyParts := strings.Split(key, "/")
	if len(keyParts) != 3 {
		return fmt.Errorf("invalid key: '%s'", key)
	}
	indexName := keyParts[0]
	indexType := keyParts[1]
	valueID := keyParts[2]

	s.checkOrCreateIndex(indexName, indexType)

	response, err := s.client.Delete().
		Index(indexName).
		Type(indexType).
		Id(valueID).
		Do(s.context)

	if response != nil && !response.Found {
		return nil
	}

	return err
}

// Flush the Elasticsearch translog to disk
func (s *ElasticsearchKeyValueStore) Flush() error {
	s.flushCounter.Inc()
	log.Println("Flusing ES indexes...")
	_, err := s.client.Flush("_all").
		WaitIfOngoing(true).
		Do(s.context)
	log.Println("Done flusing ES indexes.")
	return err
}
<commit_msg>fix import<commit_after>package kv

import (
	"encoding/json"
	"fmt"
	"log"
	"strings"

	"github.com/movio/kasper/metrics"
	"github.com/movio/kasper/util"
	"golang.org/x/net/context"
	elastic "gopkg.in/olivere/elastic.v5"
)

type ElasticsearchOpts struct {
	GetIndexSettings func(indexName string) string
	GetIndexMappings func(indexName string) string
}

var DefaultElasticsearchOpts ElasticsearchOpts = ElasticsearchOpts{
	GetIndexSettings: func(indexName string) string {
		return `{
			"index.translog.durability": "request"
		}`
	},
	GetIndexMappings: func(indexName string) string {
		return `{
			"_all" : {
				"enabled" : false
			},
			"dynamic_templates": [{
				"no_index": {
					"mapping": {
						"index": "no"
					},
					"match": "*"
				}
			}]
		}`
	},
}

type indexAndType struct {
	indexName string
	indexType string
}

// ElasticsearchKeyValueStore is a key-value storage that uses ElasticSearch.
// In this key-value store, all keys must have the format "<index>/<type>/<_id>".
type ElasticsearchKeyValueStore struct {
	elasticSearchOpts *ElasticsearchOpts
	witness           *util.StructPtrWitness
	client            *elastic.Client
	context           context.Context
	existingIndexes   []indexAndType
	metricsProvider   metrics.Provider
	getCounter        metrics.Counter
	getAllSummary     metrics.Summary
	putCounter        metrics.Counter
	putAllSummary     metrics.Summary
	deleteCounter     metrics.Counter
	flushCounter      metrics.Counter
}

// NewESKeyValueStore creates new ElasticsearchKeyValueStore instance.
// Host must of the format hostname:port.
// StructPtr should be a pointer to struct type that is used.
// for serialization and deserialization of store values.
func NewESKeyValueStore(url string, structPtr interface{}) *ElasticsearchKeyValueStore {
	return NewESKeyValueStoreWithOpts(url, structPtr, &DefaultElasticsearchOpts, &metrics.NoopMetricsProvider{})
}

// TBD
func NewESKeyValueStoreWithOpts(url string, structPtr interface{}, opts *ElasticsearchOpts, metricsProvider metrics.Provider) *ElasticsearchKeyValueStore {
	client, err := elastic.NewClient(
		elastic.SetURL(url),
		elastic.SetSniff(false), // FIXME: workaround for issues with ES in docker
	)
	if err != nil {
		panic(fmt.Sprintf("Cannot create ElasticSearch Client to '%s': %s", url, err))
	}
	return &ElasticsearchKeyValueStore{
		elasticSearchOpts: opts,
		witness:           util.NewStructPtrWitness(structPtr),
		client:            client,
		context:           context.Background(),
		existingIndexes:   nil,
		metricsProvider:   metricsProvider,
	}
}

func (s *ElasticsearchKeyValueStore) createMetrics() {
	s.getCounter = s.metricsProvider.NewCounter("ElasticsearchKeyValueStore_Get", "Number of Get() calls")
	s.getAllSummary = s.metricsProvider.NewSummary("ElasticsearchKeyValueStore_GetAll", "Summary of GetAll() calls")
	s.putCounter = s.metricsProvider.NewCounter("ElasticsearchKeyValueStore_Put", "Number of Put() calls")
	s.putAllSummary = s.metricsProvider.NewSummary("ElasticsearchKeyValueStore_PutAll", "Summary of PutAll() calls")
	s.deleteCounter = s.metricsProvider.NewCounter("ElasticsearchKeyValueStore_Delete", "Number of Delete() calls")
	s.flushCounter = s.metricsProvider.NewCounter("ElasticsearchKeyValueStore_Flush", "Summary of Flush() calls")
}

func (s *ElasticsearchKeyValueStore) checkOrCreateIndex(indexName string, indexType string) {
	for _, existing := range s.existingIndexes {
		if existing.indexName == indexName && existing.indexType == indexType {
			return
		}
	}
	exists, err := s.client.IndexExists(indexName).Do(s.context)
	if err != nil {
		panic(fmt.Sprintf("Failed to check if index exists: %s", err))
	}
	if !exists {
		_, err = s.client.
			CreateIndex(indexName).
			BodyString(s.elasticSearchOpts.GetIndexSettings(indexName)).
			Do(s.context)
		if err != nil {
			panic(fmt.Sprintf("Failed to create index: %s", err))
		}
		s.putMapping(indexName, indexType)
	}

	s.existingIndexes = append(s.existingIndexes, indexAndType{indexName, indexType})
}

func (s *ElasticsearchKeyValueStore) putMapping(indexName string, indexType string) {
	resp, err := s.client.
		PutMapping().
		Index(indexName).
		Type(indexType).
		BodyString(s.elasticSearchOpts.GetIndexMappings(indexName)).
		Do(s.context)
	if err != nil {
		panic(fmt.Sprintf("Failed to put mapping for index: %s/%s: %s", indexName, indexType, err))
	}
	if resp == nil {
		panic(fmt.Sprintf("Expected put mapping response; got: %v", resp))
	}
	if !resp.Acknowledged {
		panic(fmt.Sprintf("Expected put mapping ack; got: %v", resp.Acknowledged))
	}
}

// Get gets value by key from store
func (s *ElasticsearchKeyValueStore) Get(key string) (interface{}, error) {
	s.getCounter.Inc()
	keyParts := strings.Split(key, "/")
	if len(keyParts) != 3 {
		return nil, fmt.Errorf("invalid key: '%s'", key)
	}
	indexName := keyParts[0]
	indexType := keyParts[1]
	valueID := keyParts[2]

	s.checkOrCreateIndex(indexName, indexType)

	rawValue, err := s.client.Get().
		Index(indexName).
		Type(indexType).
		Id(valueID).
		Do(s.context)

	if fmt.Sprintf("%s", err) == "elastic: Error 404 (Not Found)" {
		return s.witness.Nil(), nil
	}

	if err != nil {
		return s.witness.Nil(), err
	}

	if !rawValue.Found {
		return s.witness.Nil(), nil
	}

	structPtr := s.witness.Allocate()
	err = json.Unmarshal(*rawValue.Source, structPtr)
	if err != nil {
		return s.witness.Nil(), err
	}
	return structPtr, nil
}

// TBD
func (s *ElasticsearchKeyValueStore) GetAll(keys []string) ([]*Entry, error) {
	s.getAllSummary.Observe(float64(len(keys)))
	multiGet := s.client.MultiGet()
	for _, key := range keys {
		keyParts := strings.Split(key, "/")
		if len(keyParts) != 3 {
			return nil, fmt.Errorf("invalid key: '%s'", key)
		}
		indexName := keyParts[0]
		indexType := keyParts[1]
		valueID := keyParts[2]

		s.checkOrCreateIndex(indexName, indexType)

		item := elastic.NewMultiGetItem().
			Index(indexName).
			Type(indexType).
			Id(valueID)

		multiGet.Add(item)
	}
	response, err := multiGet.Do(s.context)
	if err != nil {
		return nil, err
	}
	entries := make([]*Entry, len(keys))
	for i, doc := range response.Docs {
		var structPtr interface{}
		if !doc.Found {
			structPtr = s.witness.Nil()
		} else {
			structPtr = s.witness.Allocate()
			err = json.Unmarshal(*doc.Source, structPtr)
			if err != nil {
				return nil, err
			}
		}
		entries[i] = &Entry{keys[i], structPtr}
	}
	return entries, nil
}

// Put updates key in store with serialized value
func (s *ElasticsearchKeyValueStore) Put(key string, structPtr interface{}) error {
	s.witness.Assert(structPtr)
	s.putCounter.Inc()
	keyParts := strings.Split(key, "/")
	if len(keyParts) != 3 {
		return fmt.Errorf("invalid key: '%s'", key)
	}
	indexName := keyParts[0]
	indexType := keyParts[1]
	valueID := keyParts[2]

	s.checkOrCreateIndex(indexName, indexType)

	_, err := s.client.Index().
		Index(indexName).
		Type(indexType).
		Id(valueID).
		BodyJson(structPtr).
		Do(s.context)

	return err
}

// PutAll bulk executes Put operation for several entries
func (s *ElasticsearchKeyValueStore) PutAll(entries []*Entry) error {
	s.putAllSummary.Observe(float64(len(entries)))
	if len(entries) == 0 {
		return nil
	}
	bulk := s.client.Bulk()
	for _, entry := range entries {
		keyParts := strings.Split(entry.Key, "/")
		if len(keyParts) != 3 {
			return fmt.Errorf("invalid key: '%s'", entry.Key)
		}
		indexName := keyParts[0]
		indexType := keyParts[1]
		valueID := keyParts[2]

		s.witness.Assert(entry.Value)
		s.checkOrCreateIndex(indexName, indexType)

		bulk.Add(elastic.NewBulkIndexRequest().
			Index(indexName).
			Type(indexType).
			Id(valueID).
			Doc(entry.Value),
		)
	}
	_, err := bulk.Do(s.context)
	return err
}

// Delete removes key from store
func (s *ElasticsearchKeyValueStore) Delete(key string) error {
	s.deleteCounter.Inc()
	keyParts := strings.Split(key, "/")
	if len(keyParts) != 3 {
		return fmt.Errorf("invalid key: '%s'", key)
	}
	indexName := keyParts[0]
	indexType := keyParts[1]
	valueID := keyParts[2]

	s.checkOrCreateIndex(indexName, indexType)

	response, err := s.client.Delete().
		Index(indexName).
		Type(indexType).
		Id(valueID).
		Do(s.context)

	if response != nil && !response.Found {
		return nil
	}

	return err
}

// Flush the Elasticsearch translog to disk
func (s *ElasticsearchKeyValueStore) Flush() error {
	s.flushCounter.Inc()
	log.Println("Flusing ES indexes...")
	_, err := s.client.Flush("_all").
		WaitIfOngoing(true).
		Do(s.context)
	log.Println("Done flusing ES indexes.")
	return err
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "encoding/json"
    "net"
    "bufio"
)

type PeerInfo struct {
    Peer string `json:"peer"`
    Latency int `json:"latency"`
}

type UserInfo struct {
    Type string `json:"type" `
    User string `json:"user" `
    Room string `json:"room" `
    Host string `json:"host" `
    Latency []PeerInfo `json:"latency"`
}

type User struct {
    Name string `json:"name"`
    Role string `json:"role"` //enum: "host", "user"
}

type Room struct {
    ID string `json:"roomID"`
    Users []User `json:"users"`
}

func (room *Room) addUser(user User) {
    room.Users = append(room.Users, user)
}

func (room *Room) getUsers() []User {
    return room.Users
}

func (room *Room) removeUser(user User) {
    room.Users = room.Users
    for i, u range room.Users {
	if u.Name == user.Name {
	    room.Users = append(room.Users[:i], room.Users[i+1:]...) // The ... is essential
	    return
	}
    }
}

const (
    CONN_HOST = "localhost"
    CONN_PORT = "8888"
    CONN_TYPE = "tcp"
)

var rooms map(string)Room

func main() {
    // Listen for incoming connections.
    listener, err := net.Listen(CONN_TYPE, CONN_HOST+":"+CONN_PORT)
    queue := make(chan UserInfo, 10) // Buffered channel with capacity of 10
    rooms = make(map(string)Room)

    if err != nil {
	fmt.Println("Error listening:", err.Error())
    }

    // Close the listener when the application closes.
    defer listener.Close()

    for {
	// Listen for an incoming connection.
	conn, err := listener.Accept()

	if err != nil {
	    fmt.Println("Error accepting: ", err.Error())
	    continue
	}

	// Handle connections in a new goroutine.
	go handleRequest(conn, queue)
	go handleTasks(conn, queue) // Potentially need to increase the number of workers
    }
}

// Handles incoming requests.
func handleRequest(conn net.Conn, queue chan<- UserInfo) {
    defer conn.Close()

    input := bufio.NewScanner(conn)
    var userInfo UserInfo

    for input.Scan() {
	text := input.Text()
	byte_text := []byte(text)
	err := json.Unmarshal(byte_text, &userInfo)
	if err != nil {
	    continue
	}
	queue <- userInfo // send userInfo to task queue
    }
}

func handleTasks(conn net.Conn, queue <-chan UserInfo) {
    for {
	var userInfo UserInfo
	userInfo = <- queue

	switch userInfo.Type {
	case "newUser": fmt.Println("newUser")
	case "host": fmt.Println("host")
	case "disconnectedUser": fmt.Println("disconnectedUser")
	}

	fmt.Fprintf(conn, "Type: %s  User: %s  Room: %s  Host: %s", userInfo.Type, userInfo.User, userInfo.Room, userInfo.Host)
    }
}

func newUserHandler(userInfo UserInfo) {
    roomId := userInfo.Room
    if room, exist := rooms[roomId]; exist {
	user := User{Name: UserInfo.User, Role: "user"}
	room.addUser(user)
    }
}

func newHostHandler(userInfo UserInfo) {
    roomId := userInfo.Room
    if room, exist := rooms[roomId]; !exist {
	user := User{Name: UserInfo.User, Role: "host"}
	users := make([]User)
	users = append(users, user)
	room := Room{ID: roomID, Users: users]
	rooms[roomId] = room;
    }
}

func disconnectHandler(userInfo UserInfo) {
    roomId := UserInfo.Room
    if room, exist := rooms[roomId]; exist {
	room.removeUser(userInfo.User)
	if len(room.getUsers())==0 {
	    delete(rooms, roomId)
	}
    }
}<commit_msg>Modify route.go<commit_after>package main

import (
    "fmt"
    "encoding/json"
    "net"
    "bufio"
)

type PeerInfo struct {
    Peer string `json:"peer"`
    Latency int `json:"latency"`
}

type UserInfo struct {
    Type string `json:"type" `
    User string `json:"user" `
    Room string `json:"room" `
    Host string `json:"host" `
    Latency []PeerInfo `json:"latency"`
}

type User struct {
    Name string `json:"name"`
    Role string `json:"role"` //enum: "host", "user"
}

type Room struct {
    ID string `json:"roomID"`
    Users []User `json:"users"`
}

func (room *Room) addUser(user User) {
    room.Users = append(room.Users, user)
}

func (room *Room) getUsers() []User {
    return room.Users
}

func (room *Room) removeUser(user User) {
    room.Users = room.Users
    for i, u := range room.Users {
	if u.Name == user.Name {
	    room.Users = append(room.Users[:i], room.Users[i+1:]...) // The ... is essential
	    return
	}
    }
}

const (
    CONN_HOST = "localhost"
    CONN_PORT = "8888"
    CONN_TYPE = "tcp"
)

var rooms map[string]Room

func main() {
    // Listen for incoming connections.
    listener, err := net.Listen(CONN_TYPE, CONN_HOST+":"+CONN_PORT)
    queue := make(chan UserInfo, 10) // Buffered channel with capacity of 10
    rooms = make(map[string]Room)

    if err != nil {
	fmt.Println("Error listening:", err.Error())
    }

    // Close the listener when the application closes.
    defer listener.Close()

    for {
	// Listen for an incoming connection.
	conn, err := listener.Accept()

	if err != nil {
	    fmt.Println("Error accepting: ", err.Error())
	    continue
	}

	// Handle connections in a new goroutine.
	go handleRequest(conn, queue)
	go handleTasks(conn, queue) // Potentially need to increase the number of workers
    }
}

// Handles incoming requests.
func handleRequest(conn net.Conn, queue chan<- UserInfo) {
    defer conn.Close()

    input := bufio.NewScanner(conn)
    var userInfo UserInfo

    for input.Scan() {
	text := input.Text()
	byte_text := []byte(text)
	err := json.Unmarshal(byte_text, &userInfo)
	if err != nil {
	    continue
	}
	queue <- userInfo // send userInfo to task queue
    }
}

func handleTasks(conn net.Conn, queue <-chan UserInfo) {
    for {
	var userInfo UserInfo
	userInfo = <- queue

	switch userInfo.Type {
	case "newUser": fmt.Println("newUser")
	case "host": fmt.Println("host")
	case "disconnectedUser": fmt.Println("disconnectedUser")
	}

	fmt.Fprintf(conn, "Type: %s  User: %s  Room: %s  Host: %s", userInfo.Type, userInfo.User, userInfo.Room, userInfo.Host)
    }
}

func newUserHandler(userInfo UserInfo) {
    roomId := userInfo.Room
    if room, exist := rooms[roomId]; exist {
	user := User{Name: userInfo.User, Role: "user"}
	room.addUser(user)
    }
}

func newHostHandler(userInfo UserInfo) {
    roomId := userInfo.Room
    if room, exist := rooms[roomId]; !exist {
	user := User{Name: userInfo.User, Role: "host"}
	users := make([]User, 0)
	users = append(users, user)
	room := Room{ID: roomId, Users: users}
	rooms[roomId] = room;
    }
}

func disconnectHandler(userInfo UserInfo) {
    roomId := userInfo.Room
    if room, exist := rooms[roomId]; exist {
	room.removeUser(userInfo.User)
	if len(room.getUsers())==0 {
	    delete(rooms, roomId)
	}
    }
}<|endoftext|>


<commit_before>package peel

import "github.com/mediocregopher/bananaq/core"

// TODO need to make sure everything all fields here are alphanumeric, or maybe
// change the separator in redis to be \0 or something

// Keeps track of events which are available to be retrieved by any particular
// consumer group, with scores corresponding to the event's id.
func queueAvailableByID(queue string) core.Key {
	return core.Key{Base: queue, Subs: []string{"available", "id"}}
}

// Keeps track of events which are available to be retrieved by any particular
// consumer group, with scores corresponding to the event's expire time.
func queueAvailableByExpire(queue string) core.Key {
	return core.Key{Base: queue, Subs: []string{"available", "expire"}}
}

// Keeps track of events that are currently in progress, with scores
// corresponding to the event's id. Used to retrieve the next available event in
// a queue
func queueInProgressByID(queue, cgroup string) core.Key {
	return core.Key{Base: queue, Subs: []string{cgroup, "inprogress", "id"}}
}

// Keeps track of events that are currently in progress, with scores
// corresponding to the event's ack deadline. Used to timeout in progress events
// and put them in redo
func queueInProgressByAck(queue, cgroup string) core.Key {
	return core.Key{Base: queue, Subs: []string{cgroup, "inprogress", "ack"}}
}

// Keeps track of events which were previously attempted to be processed but
// failed. Score is the event's id
func queueRedo(queue, cgroup string) core.Key {
	return core.Key{Base: queue, Subs: []string{cgroup, "redo"}}
}

// Keeps track of events which have been successfully processed. Score is the
// event's id
func queueDone(queue, cgroup string) core.Key {
	return core.Key{Base: queue, Subs: []string{cgroup, "done"}}
}

// Keeps track of all events currently in use by a cgroup which haven't expired.
// Used in order to clean expired events out of a group
func queueInUseByExpire(queue, cgroup string) core.Key {
	return core.Key{Base: queue, Subs: []string{cgroup, "inuse"}}
}
<commit_msg>clean up data being passed from peel to core<commit_after>package peel

import (
	"encoding/hex"

	"github.com/mediocregopher/bananaq/core"
)

func queueNameClean(queue, cgroup string) (string, string) {
	return hex.EncodeToString([]byte(queue)), hex.EncodeToString([]byte(cgroup))
}

// Keeps track of events which are available to be retrieved by any particular
// consumer group, with scores corresponding to the event's id.
func queueAvailableByID(queue string) core.Key {
	queue, _ = queueNameClean(queue, "")
	return core.Key{Base: queue, Subs: []string{"available", "id"}}
}

// Keeps track of events which are available to be retrieved by any particular
// consumer group, with scores corresponding to the event's expire time.
func queueAvailableByExpire(queue string) core.Key {
	queue, _ = queueNameClean(queue, "")
	return core.Key{Base: queue, Subs: []string{"available", "expire"}}
}

// Keeps track of events that are currently in progress, with scores
// corresponding to the event's id. Used to retrieve the next available event in
// a queue
func queueInProgressByID(queue, cgroup string) core.Key {
	queue, cgroup = queueNameClean(queue, cgroup)
	return core.Key{Base: queue, Subs: []string{cgroup, "inprogress", "id"}}
}

// Keeps track of events that are currently in progress, with scores
// corresponding to the event's ack deadline. Used to timeout in progress events
// and put them in redo
func queueInProgressByAck(queue, cgroup string) core.Key {
	queue, cgroup = queueNameClean(queue, cgroup)
	return core.Key{Base: queue, Subs: []string{cgroup, "inprogress", "ack"}}
}

// Keeps track of events which were previously attempted to be processed but
// failed. Score is the event's id
func queueRedo(queue, cgroup string) core.Key {
	queue, cgroup = queueNameClean(queue, cgroup)
	return core.Key{Base: queue, Subs: []string{cgroup, "redo"}}
}

// Keeps track of events which have been successfully processed. Score is the
// event's id
func queueDone(queue, cgroup string) core.Key {
	queue, cgroup = queueNameClean(queue, cgroup)
	return core.Key{Base: queue, Subs: []string{cgroup, "done"}}
}

// Keeps track of all events currently in use by a cgroup which haven't expired.
// Used in order to clean expired events out of a group
func queueInUseByExpire(queue, cgroup string) core.Key {
	queue, cgroup = queueNameClean(queue, cgroup)
	return core.Key{Base: queue, Subs: []string{cgroup, "inuse"}}
}
<|endoftext|>


<commit_before>package address

// Basic imports
import (
	"testing"

	"github.com/stretchr/testify/suite"
)

// AddressTestSuite -
type AddressTestSuite struct {
	suite.Suite
}

func (suite *AddressTestSuite) SetupTest() {
}

func (suite *AddressTestSuite) TestNewAddressSuccess() {
	addrString := "foo/bar"
	addr := NewAddress(addrString)
	suite.Assert().Equal([]string{
		"foo",
		"bar",
	}, addr.stack)
	suite.Assert().Equal(0, addr.index)
}

func (suite *AddressTestSuite) TestCurrentSuccess() {
	addrString := "foo/bar"
	addr := NewAddress(addrString)

	part := addr.Pop()
	suite.Assert().Equal("foo", part)
	suite.Assert().Equal(1, addr.index)

	part = addr.Current()
	suite.Assert().Equal("bar", part)
	suite.Assert().Equal(1, addr.index)
}

func (suite *AddressTestSuite) TestCurrentParamsSuccess() {
	addrString := "foo:bar"
	addr := NewAddress(addrString)

	params := addr.CurrentParams()
	suite.Assert().Equal("bar", params)
	suite.Assert().Equal(0, addr.index)
}

func (suite *AddressTestSuite) TestCurrentParamsEmptySuccess() {
	addrString := "foo"
	addr := NewAddress(addrString)

	params := addr.CurrentParams()
	suite.Assert().Equal("", params)
	suite.Assert().Equal(0, addr.index)
}

func (suite *AddressTestSuite) TestCurrentProtocolSuccess() {
	addrString := "foo:bar"
	addr := NewAddress(addrString)

	protocol := addr.CurrentProtocol()
	suite.Assert().Equal("foo", protocol)
	suite.Assert().Equal(0, addr.index)
}

func (suite *AddressTestSuite) TestPopSuccess() {
	addrString := "foo/bar"
	addr := NewAddress(addrString)

	part := addr.Pop()
	suite.Assert().Equal("foo", part)
	suite.Assert().Equal(1, addr.index)

	part = addr.Pop()
	suite.Assert().Equal("bar", part)
	suite.Assert().Equal(2, addr.index)

	part = addr.Pop()
	suite.Assert().Equal("", part)
	suite.Assert().Equal(2, addr.index)
}

func (suite *AddressTestSuite) TestAppendSuccess() {
	addrString := "foo"
	addr := NewAddress(addrString)
	suite.Assert().Len(addr.stack, 1)
	suite.Assert().Equal(addr.stack[0], "foo")

	addr.Append("bar")
	suite.Assert().Len(addr.stack, 2)
	suite.Assert().Equal(addr.stack[1], "bar")
}

func (suite *AddressTestSuite) TestRemainingSuccess() {
	addrString := "foo/bar/more"
	addr := NewAddress(addrString)

	part := addr.Pop()
	suite.Assert().Equal("foo", part)
	suite.Assert().Equal(1, addr.index)

	remaining := addr.Remaining()
	suite.Assert().Equal([]string{"bar", "more"}, remaining)
	suite.Assert().Equal(1, addr.index)
}

func (suite *AddressTestSuite) TestProcessedSuccess() {
	addrString := "foo/bar/more"
	addr := NewAddress(addrString)

	part := addr.Pop()
	suite.Assert().Equal("foo", part)
	suite.Assert().Equal(1, addr.index)

	processed := addr.Processed()
	suite.Assert().Equal([]string{"foo"}, processed)
	suite.Assert().Equal(1, addr.index)

	addr.Pop()
	processed = addr.Processed()
	suite.Assert().Equal([]string{"foo", "bar"}, processed)
	suite.Assert().Equal(2, addr.index)
}

func (suite *AddressTestSuite) TestRemainingStringSuccess() {
	addrString := "foo/bar/more"
	addr := NewAddress(addrString)

	part := addr.Pop()
	suite.Assert().Equal("foo", part)
	suite.Assert().Equal(1, addr.index)

	remaining := addr.RemainingString()
	suite.Assert().Equal("bar/more", remaining)
	suite.Assert().Equal(1, addr.index)
}

func (suite *AddressTestSuite) TestResetSuccess() {
	addrString := "foo/bar"
	addr := NewAddress(addrString)

	part := addr.Pop()
	suite.Assert().Equal("foo", part)
	suite.Assert().Equal(1, addr.index)

	addr.Reset()
	suite.Assert().Equal(0, addr.index)
}

func (suite *AddressTestSuite) TestStringSuccess() {
	addrString := "foo/bar"
	addr := NewAddress(addrString)

	retAddr := addr.String()
	suite.Assert().Equal(addrString, retAddr)
}

func TestAddressTestSuite(t *testing.T) {
	suite.Run(t, new(AddressTestSuite))
}
<commit_msg>Add RemainingProtocols test<commit_after>package address

// Basic imports
import (
	"testing"

	"github.com/stretchr/testify/suite"
)

// AddressTestSuite -
type AddressTestSuite struct {
	suite.Suite
}

func (suite *AddressTestSuite) SetupTest() {
}

func (suite *AddressTestSuite) TestNewAddressSuccess() {
	addrString := "foo/bar"
	addr := NewAddress(addrString)
	suite.Assert().Equal([]string{
		"foo",
		"bar",
	}, addr.stack)
	suite.Assert().Equal(0, addr.index)
}

func (suite *AddressTestSuite) TestCurrentSuccess() {
	addrString := "foo/bar"
	addr := NewAddress(addrString)

	part := addr.Pop()
	suite.Assert().Equal("foo", part)
	suite.Assert().Equal(1, addr.index)

	part = addr.Current()
	suite.Assert().Equal("bar", part)
	suite.Assert().Equal(1, addr.index)
}

func (suite *AddressTestSuite) TestCurrentParamsSuccess() {
	addrString := "foo:bar"
	addr := NewAddress(addrString)

	params := addr.CurrentParams()
	suite.Assert().Equal("bar", params)
	suite.Assert().Equal(0, addr.index)
}

func (suite *AddressTestSuite) TestCurrentParamsEmptySuccess() {
	addrString := "foo"
	addr := NewAddress(addrString)

	params := addr.CurrentParams()
	suite.Assert().Equal("", params)
	suite.Assert().Equal(0, addr.index)
}

func (suite *AddressTestSuite) TestCurrentProtocolSuccess() {
	addrString := "foo:bar"
	addr := NewAddress(addrString)

	protocol := addr.CurrentProtocol()
	suite.Assert().Equal("foo", protocol)
	suite.Assert().Equal(0, addr.index)
}

func (suite *AddressTestSuite) TestPopSuccess() {
	addrString := "foo/bar"
	addr := NewAddress(addrString)

	part := addr.Pop()
	suite.Assert().Equal("foo", part)
	suite.Assert().Equal(1, addr.index)

	part = addr.Pop()
	suite.Assert().Equal("bar", part)
	suite.Assert().Equal(2, addr.index)

	part = addr.Pop()
	suite.Assert().Equal("", part)
	suite.Assert().Equal(2, addr.index)
}

func (suite *AddressTestSuite) TestAppendSuccess() {
	addrString := "foo"
	addr := NewAddress(addrString)
	suite.Assert().Len(addr.stack, 1)
	suite.Assert().Equal(addr.stack[0], "foo")

	addr.Append("bar")
	suite.Assert().Len(addr.stack, 2)
	suite.Assert().Equal(addr.stack[1], "bar")
}

func (suite *AddressTestSuite) TestRemainingSuccess() {
	addrString := "foo/bar/more"
	addr := NewAddress(addrString)

	part := addr.Pop()
	suite.Assert().Equal("foo", part)
	suite.Assert().Equal(1, addr.index)

	remaining := addr.Remaining()
	suite.Assert().Equal([]string{"bar", "more"}, remaining)
	suite.Assert().Equal(1, addr.index)
}

func (suite *AddressTestSuite) TestProcessedSuccess() {
	addrString := "foo/bar/more"
	addr := NewAddress(addrString)

	part := addr.Pop()
	suite.Assert().Equal("foo", part)
	suite.Assert().Equal(1, addr.index)

	processed := addr.Processed()
	suite.Assert().Equal([]string{"foo"}, processed)
	suite.Assert().Equal(1, addr.index)

	addr.Pop()
	processed = addr.Processed()
	suite.Assert().Equal([]string{"foo", "bar"}, processed)
	suite.Assert().Equal(2, addr.index)
}

func (suite *AddressTestSuite) TestRemainingStringSuccess() {
	addrString := "foo/bar/more"
	addr := NewAddress(addrString)

	part := addr.Pop()
	suite.Assert().Equal("foo", part)
	suite.Assert().Equal(1, addr.index)

	remaining := addr.RemainingString()
	suite.Assert().Equal("bar/more", remaining)
	suite.Assert().Equal(1, addr.index)
}

func (suite *AddressTestSuite) TestRemainingProtocolsSuccess() {
	addrString := "foo:param/bar:param/more:param"
	addr := NewAddress(addrString)

	remaining := addr.RemainingProtocols()
	suite.Assert().Equal([]string{"foo", "bar", "more"}, remaining)
}

func (suite *AddressTestSuite) TestResetSuccess() {
	addrString := "foo/bar"
	addr := NewAddress(addrString)

	part := addr.Pop()
	suite.Assert().Equal("foo", part)
	suite.Assert().Equal(1, addr.index)

	addr.Reset()
	suite.Assert().Equal(0, addr.index)
}

func (suite *AddressTestSuite) TestStringSuccess() {
	addrString := "foo/bar"
	addr := NewAddress(addrString)

	retAddr := addr.String()
	suite.Assert().Equal(addrString, retAddr)
}

func TestAddressTestSuite(t *testing.T) {
	suite.Run(t, new(AddressTestSuite))
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"time"

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

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

func NewEbsVolumeDriver() (VolumeDriver, error) {
	d := &ebsVolumeDriver{
		volumes: make(map[string]string),
	}
	d.ec2meta = ec2metadata.New(nil)

	// Fetch AWS information, validating along the way.
	if !d.ec2meta.Available() {
		return nil, errors.New("Not running on an EC2 instance.")
	}
	var err error
	if d.awsInstanceId, err = d.ec2meta.GetMetadata("instance-id"); err != nil {
		return nil, err
	}
	if d.awsRegion, err = d.ec2meta.Region(); err != nil {
		return nil, err
	}
	if d.awsAvailabilityZone, err =
		d.ec2meta.GetMetadata("placement/availability-zone"); err != nil {
		return nil, err
	}

	d.ec2 = ec2.New(aws.NewConfig().WithRegion(d.awsRegion))

	// Print some diagnostic information and then return the driver.
	log("Auto-detected EC2 information:\n")
	log("\tInstanceId        : %v\n", d.awsInstanceId)
	log("\tRegion            : %v\n", d.awsRegion)
	log("\tAvailability Zone : %v\n", d.awsAvailabilityZone)
	return d, nil
}

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

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

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

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

	return d.doMount(name)
}

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

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

	return m, nil
}

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

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

	delete(d.volumes, name)
	return nil
}

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

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

	return nil
}

func (d *ebsVolumeDriver) doMount(name string) (string, error) {
	// Auto-generate a random mountpoint.
	mnt := "/mnt/blocker/" + uuid.NewV4().String()

	// Ensure the directory /mnt/blocker/<m> exists.
	if err := os.MkdirAll(mnt, os.ModeDir|0700); err != nil {
		return "", err
	}
	if stat, err := os.Stat(mnt); err != nil || !stat.IsDir() {
		return "", fmt.Errorf("Mountpoint %v is not a directory: %v", mnt, err)
	}

	// Attach the EBS device to the current EC2 instance.
	dev, err := d.attachVolume(name)
	if err != nil {
		return "", err
	}

	// Now go ahead and mount the EBS device to the desired mountpoint.
	// TODO: support encrypted filesystems.
	if out, err := exec.Command("mount", dev, mnt).CombinedOutput(); err != nil {
		// Make sure to detach the instance before quitting (ignoring errors).
		d.detachVolume(name)

		return "", fmt.Errorf("Mounting device %v to %v failed: %v\n%v",
			dev, mnt, err, string(out))
	}

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

func (d *ebsVolumeDriver) attachVolume(name string) (string, error) {
	// Now find the first free device to attach the EBS volume to.  See
	// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html
	// for recommended naming scheme (/dev/sd[f-p]).
	for _, c := range "fghijklmnop" {
		dev := "/dev/sd" + string(c)

		// TODO: we could check locally first to eliminate a few network
		//     roundtrips in the event that some devices are used.  Even if we
		//     did that, however, we'd need the checks regarding the AWS request
		//     failing below, because of TOCTOU.

		if _, err := d.ec2.AttachVolume(&ec2.AttachVolumeInput{
			Device:     aws.String(dev),
			InstanceId: aws.String(d.awsInstanceId),
			VolumeId:   aws.String(name),
		}); err != nil {
			if awsErr, ok := err.(awserr.Error); ok &&
				awsErr.Code() == "InvalidParameterValue" {
				// If AWS is simply reporting that the device is already in
				// use, then go ahead and check the next one.
				continue
			}

			return "", err
		}

		// The attach operation is asynchronous, so we now need to wait until
		// it completes before proceeding to the mount.  Sadly, this requires
		// some clunky retries, sleeps, and that kind of crap.
		tries := 0
		for {
			tries++

			volumes, err := d.ec2.DescribeVolumes(&ec2.DescribeVolumesInput{
				VolumeIds: []*string{aws.String(name)},
			})
			if err != nil {
				return "", err
			}

			volume := volumes.Volumes[0]
			var attachment *ec2.VolumeAttachment
			if len(volume.Attachments) == 1 {
				attachment = volume.Attachments[0]
				if *attachment.State == ec2.VolumeAttachmentStateAttached {
					// Good to go, we're all attached.
					break
				}
			}

			if tries == 12 {
				if attachment == nil {
					return "", fmt.Errorf(
						"Volume attach failed: expected 1 attachment, got %v",
						len(volume.Attachments))
				} else {
					return "", fmt.Errorf(
						"Volume attach failed: state is %v", *attachment.State)
				}
			}

			log("\tWaiting for EBS attach to complete...\n")
			time.Sleep(5 * time.Second)
		}

		// Finally, the attach is complete.
		log("\tAttached EBS volume %v to %v:%v.\n", name, d.awsInstanceId, dev)
		if _, err := os.Lstat(dev); os.IsNotExist(err) {
			// On newer Linux kernels, /dev/sd* is mapped to /dev/xvd*.  See
			// if that's the case.
			altdev := "/dev/xvd" + string(c)
			if _, err := os.Lstat(altdev); os.IsNotExist(err) {
				d.detachVolume(name)
				return "", fmt.Errorf("Device %v is missing after attach.", dev)
			}

			log("\tLocal device name is %v\n", altdev)
			dev = altdev
		}

		return dev, nil
	}

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

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

	// First unmount the device.
	if out, err := exec.Command("umount", mnt).CombinedOutput(); err != nil {
		return fmt.Errorf("Unmounting %v failed: %v\n%v", mnt, err, string(out))
	}

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

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

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

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

	log("\tDetached EBS volume %v from %v.\n", name, d.awsInstanceId)
	return nil
}
<commit_msg>Wait for volume to become available before mounting<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"time"

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

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

func NewEbsVolumeDriver() (VolumeDriver, error) {
	d := &ebsVolumeDriver{
		volumes: make(map[string]string),
	}
	d.ec2meta = ec2metadata.New(nil)

	// Fetch AWS information, validating along the way.
	if !d.ec2meta.Available() {
		return nil, errors.New("Not running on an EC2 instance.")
	}
	var err error
	if d.awsInstanceId, err = d.ec2meta.GetMetadata("instance-id"); err != nil {
		return nil, err
	}
	if d.awsRegion, err = d.ec2meta.Region(); err != nil {
		return nil, err
	}
	if d.awsAvailabilityZone, err =
		d.ec2meta.GetMetadata("placement/availability-zone"); err != nil {
		return nil, err
	}

	d.ec2 = ec2.New(aws.NewConfig().WithRegion(d.awsRegion))

	// Print some diagnostic information and then return the driver.
	log("Auto-detected EC2 information:\n")
	log("\tInstanceId        : %v\n", d.awsInstanceId)
	log("\tRegion            : %v\n", d.awsRegion)
	log("\tAvailability Zone : %v\n", d.awsAvailabilityZone)
	return d, nil
}

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

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

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

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

	return d.doMount(name)
}

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

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

	return m, nil
}

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

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

	delete(d.volumes, name)
	return nil
}

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

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

	return nil
}

func (d *ebsVolumeDriver) doMount(name string) (string, error) {
	// Auto-generate a random mountpoint.
	mnt := "/mnt/blocker/" + uuid.NewV4().String()

	// Ensure the directory /mnt/blocker/<m> exists.
	if err := os.MkdirAll(mnt, os.ModeDir|0700); err != nil {
		return "", err
	}
	if stat, err := os.Stat(mnt); err != nil || !stat.IsDir() {
		return "", fmt.Errorf("Mountpoint %v is not a directory: %v", mnt, err)
	}

	// Attach the EBS device to the current EC2 instance.
	dev, err := d.attachVolume(name)
	if err != nil {
		return "", err
	}

	// Now go ahead and mount the EBS device to the desired mountpoint.
	// TODO: support encrypted filesystems.
	if out, err := exec.Command("mount", dev, mnt).CombinedOutput(); err != nil {
		// Make sure to detach the instance before quitting (ignoring errors).
		d.detachVolume(name)

		return "", fmt.Errorf("Mounting device %v to %v failed: %v\n%v",
			dev, mnt, err, string(out))
	}

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

func (d *ebsVolumeDriver) waitUntilState(name, state string) error {
	// Most volume operations are asynchronous, and we often need to wait until
	// state transitions finish before proceeding to the mount.  Sadly, this
	// requires some clunky retries, sleeps, and that kind of crap.
	tries := 0
	for {
		tries++

		volumes, err := d.ec2.DescribeVolumes(&ec2.DescribeVolumesInput{
			VolumeIds: []*string{aws.String(name)},
		})
		if err != nil {
			return err
		}

		volume := volumes.Volumes[0]
		var attachment *ec2.VolumeAttachment
		if len(volume.Attachments) == 1 {
			attachment = volume.Attachments[0]
			if *attachment.State == state {
				// Good to go.
				break
			}
		}

		if tries == 12 {
			if attachment == nil {
				return fmt.Errorf(
					"Volume state transition failed: expected 1 attachment, got %v",
					len(volume.Attachments))
			} else {
				return fmt.Errorf(
					"Volume state transition failed: seeking %v, current is %v",
					state, *attachment.State)
			}
		}

		log("\tWaiting for EBS attach to complete...\n")
		time.Sleep(5 * time.Second)
	}

	return nil
}

func (d *ebsVolumeDriver) waitUntilAttached(name string) error {
	return d.waitUntilState(name, ec2.VolumeAttachmentStateAttached)
}

func (d *ebsVolumeDriver) waitUntilAvailable(name string) error {
	return d.waitUntilState(name, ec2.VolumeAttachmentStateDetached)
}

func (d *ebsVolumeDriver) attachVolume(name string) (string, error) {
	// Since detaching is asynchronous, we want to check first to see if the
	// target volume is in the process of being detached.  If it is, we'll wait
	// a little bit until it's ready to use.
	err := d.waitUntilAvailable(name)
	if err != nil {
		return "", err
	}

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

		// TODO: we could check locally first to eliminate a few network
		//     roundtrips in the event that some devices are used.  Even if we
		//     did that, however, we'd need the checks regarding the AWS request
		//     failing below, because of TOCTOU.

		if _, err := d.ec2.AttachVolume(&ec2.AttachVolumeInput{
			Device:     aws.String(dev),
			InstanceId: aws.String(d.awsInstanceId),
			VolumeId:   aws.String(name),
		}); err != nil {
			if awsErr, ok := err.(awserr.Error); ok &&
				awsErr.Code() == "InvalidParameterValue" {
				// If AWS is simply reporting that the device is already in
				// use, then go ahead and check the next one.
				continue
			}

			return "", err
		}

		err = d.waitUntilAttached(name)
		if err != nil {
			return "", err
		}

		// Finally, the attach is complete.
		log("\tAttached EBS volume %v to %v:%v.\n", name, d.awsInstanceId, dev)
		if _, err := os.Lstat(dev); os.IsNotExist(err) {
			// On newer Linux kernels, /dev/sd* is mapped to /dev/xvd*.  See
			// if that's the case.
			altdev := "/dev/xvd" + string(c)
			if _, err := os.Lstat(altdev); os.IsNotExist(err) {
				d.detachVolume(name)
				return "", fmt.Errorf("Device %v is missing after attach.", dev)
			}

			log("\tLocal device name is %v\n", altdev)
			dev = altdev
		}

		return dev, nil
	}

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

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

	// First unmount the device.
	if out, err := exec.Command("umount", mnt).CombinedOutput(); err != nil {
		return fmt.Errorf("Unmounting %v failed: %v\n%v", mnt, err, string(out))
	}

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

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

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

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

	log("\tDetached EBS volume %v from %v.\n", name, d.awsInstanceId)
	return nil
}
<|endoftext|>


<commit_before>package weave

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"io"
	"sync"
)

type PeerCache struct {
	sync.RWMutex
	table map[PeerName]*Peer
	onGC  func(*Peer)
}

func NewPeerCache(onGC func(*Peer)) *PeerCache {
	return &PeerCache{table: make(map[PeerName]*Peer), onGC: onGC}
}

func (cache *PeerCache) FetchWithDefault(peer *Peer) *Peer {
	cache.RLock()
	res, found := cache.fetchAlias(peer)
	cache.RUnlock()
	if found {
		return res
	}
	cache.Lock()
	defer cache.Unlock()
	res, found = cache.fetchAlias(peer)
	if found {
		return res
	}
	cache.table[peer.Name] = peer
	peer.IncrementLocalRefCount()
	return peer
}

func (cache *PeerCache) Fetch(name PeerName) (*Peer, bool) {
	cache.RLock()
	defer cache.RUnlock()
	peer, found := cache.table[name]
	return peer, found // GRRR, why can't I inline this!?
}

func (cache *PeerCache) ForEach(fun func(PeerName, *Peer)) {
	cache.RLock()
	defer cache.RUnlock()
	for name, peer := range cache.table {
		fun(name, peer)
	}
}

func (cache *PeerCache) ApplyUpdate(update []byte, router *Router) ([]byte, error) {
	cache.Lock()

	newPeers, decodedUpdate, decodedConns, err := cache.decodeUpdate(update, router)
	if err != nil {
		cache.Unlock()
		return nil, err
	}

	// By this point, we know the update doesn't refer to any peers we
	// have no knowledge of. We can now apply the update. Start by
	// adding in any new peers into the cache.
	for name, newPeer := range newPeers {
		cache.table[name] = newPeer
	}

	// Now apply the updates
	newUpdate := cache.applyUpdate(decodedUpdate, decodedConns, router)

	for _, peerRemoved := range cache.garbageCollect(router.Ourself) {
		delete(newUpdate, peerRemoved.Name)
	}

	// Don't need to hold cache lock any longer
	cache.Unlock()

	return encodePeersMap(newUpdate), nil
}

func (cache *PeerCache) EncodeAllPeers() []byte {
	cache.RLock()
	defer cache.RUnlock()
	return encodePeersMap(cache.table)
}

func EncodePeers(peers ...*Peer) []byte {
	buf := new(bytes.Buffer)
	enc := gob.NewEncoder(buf)
	for _, peer := range peers {
		peer.encodePeer(enc)
	}
	return buf.Bytes()
}

func (cache *PeerCache) GarbageCollect(router *Router) []*Peer {
	cache.Lock()
	defer cache.Unlock()
	return cache.garbageCollect(router.Ourself)
}

func (cache *PeerCache) String() string {
	var buf bytes.Buffer
	cache.ForEach(func(name PeerName, peer *Peer) {
		buf.WriteString(fmt.Sprint(peer, "\n"))
		peer.ForEachConnection(func(remoteName PeerName, _ Connection) {
			buf.WriteString(fmt.Sprintf("   -> %v\n", remoteName))
		})
	})
	return buf.String()
}

func (cache *PeerCache) fetchAlias(peer *Peer) (*Peer, bool) {
	if existingPeer, found := cache.table[peer.Name]; found {
		if existingPeer.UID == peer.UID {
			existingPeer.IncrementLocalRefCount()
			return existingPeer, true
		} else {
			return nil, true
		}
	}
	return nil, false
}

func (cache *PeerCache) garbageCollect(ourself *Peer) []*Peer {
	removed := []*Peer{}
	for name, peer := range cache.table {
		found, _ := ourself.HasPathTo(peer, false)
		if !found && !peer.IsLocallyReferenced() {
			cache.onGC(peer)
			delete(cache.table, name)
			ourself.Router.Macs.Delete(peer)
			removed = append(removed, peer)
		}
	}
	return removed
}

func encodePeersMap(peers map[PeerName]*Peer) []byte {
	buf := new(bytes.Buffer)
	enc := gob.NewEncoder(buf)
	for _, peer := range peers {
		peer.encodePeer(enc)
	}
	return buf.Bytes()
}

func (cache *PeerCache) decodeUpdate(update []byte, router *Router) (newPeers map[PeerName]*Peer, decodedUpdate []*Peer, decodedConns [][]byte, err error) {
	newPeers = make(map[PeerName]*Peer)
	decodedUpdate = []*Peer{}
	decodedConns = [][]byte{}

	updateBuf := new(bytes.Buffer)
	updateBuf.Write(update)
	decoder := gob.NewDecoder(updateBuf)

	for {
		nameByte, uid, version, connsBuf, decErr := decodePeerNoConns(decoder)
		if decErr == io.EOF {
			break
		} else if decErr != nil {
			err = decErr
			return
		}
		name := PeerNameFromBin(nameByte)
		newPeer := NewPeer(name, uid, version, router)
		decodedUpdate = append(decodedUpdate, newPeer)
		decodedConns = append(decodedConns, connsBuf)
		existingPeer, found := cache.table[name]
		if !found {
			newPeers[name] = newPeer
		} else if existingPeer.UID != newPeer.UID {
			err = NameCollisionError{Name: newPeer.Name}
			return
		}
	}

	unknownPeers := false
	for _, connsBuf := range decodedConns {
		connsIterator(connsBuf, func(remoteNameByte []byte, _ string) {
			remoteName := PeerNameFromBin(remoteNameByte)
			if _, found := newPeers[remoteName]; found {
				return
			}
			if _, found := cache.table[remoteName]; found {
				return
			}
			// Update refers to a peer which we have no knowledge
			// of. Thus we can't apply the update. Abort.
			unknownPeers = true
		})
	}
	if unknownPeers {
		err = UnknownPeersError{}
		return
	}
	return
}

func (cache *PeerCache) applyUpdate(decodedUpdate []*Peer, decodedConns [][]byte, router *Router) map[PeerName]*Peer {
	newUpdate := make(map[PeerName]*Peer)
	for idx, newPeer := range decodedUpdate {
		connsBuf := decodedConns[idx]
		name := newPeer.Name
		// guaranteed to find peer in the cache.table
		peer := cache.table[name]
		if peer != newPeer {
			if peer.Version() > newPeer.Version() {
				// we know more about this one than the update. If
				// peer is ourself, this is slightly racey (further
				// changes could occur to ourself in the mean
				// time). But it doesn't matter as we know that we're
				// already > newPeer.Version() and that's not going to
				// change.
				newUpdate[name] = peer
				continue
			} else if peer == router.Ourself {
				// nobody but us updates us
				continue
			} else if peer.Version() == newPeer.Version() {
				// implication is that connections are equal too
				continue
			}
		}
		// If we're here, either it was a new peer, or the update has
		// more info about the peer than we do. Either case, we need
		// to set version and conns and include the updated peer in
		// the outgoing update.

		// Can peer have been updated by anyone else in the mean time?
		// No - we know that peer is not ourself, so the only prospect
		// for an update would be someone else calling
		// router.Peers.ApplyUpdate. But ApplyUpdate takes the Lock on
		// the peers cache, so there can be no race here.
		conns := readConnsMap(peer, connsBuf, cache.table)
		peer.SetVersionAndConnections(newPeer.Version(), conns)
		newUpdate[name] = peer
	}
	return newUpdate
}

func (peer *Peer) encodePeer(enc *gob.Encoder) {
	peer.RLock()
	defer peer.RUnlock()

	checkFatal(enc.Encode(peer.NameByte))
	checkFatal(enc.Encode(peer.UID))
	checkFatal(enc.Encode(peer.version))

	connsBuf := new(bytes.Buffer)
	connsEnc := gob.NewEncoder(connsBuf)
	for _, conn := range peer.connections {
		// DANGER holding rlock on peer, going to take rlock on conn
		if !conn.Established() {
			continue
		}
		checkFatal(connsEnc.Encode(conn.Remote().NameByte))
		checkFatal(connsEnc.Encode(conn.RemoteTCPAddr()))
	}
	checkFatal(enc.Encode(connsBuf.Bytes()))
}

func decodePeerNoConns(dec *gob.Decoder) (nameByte []byte, uid uint64, version uint64, conns []byte, err error) {
	err = dec.Decode(&nameByte)
	if err != nil {
		return
	}
	err = dec.Decode(&uid)
	if err != nil {
		return
	}
	err = dec.Decode(&version)
	if err != nil {
		return
	}
	err = dec.Decode(&conns)
	if err == io.EOF {
		err = nil
	}
	return
}

func connsIterator(input []byte, fun func([]byte, string)) {
	buf := new(bytes.Buffer)
	buf.Write(input)
	dec := gob.NewDecoder(buf)
	for {
		var nameByte []byte
		err := dec.Decode(&nameByte)
		if err == io.EOF {
			return
		}
		checkFatal(err)
		var foundAt string
		checkFatal(dec.Decode(&foundAt))
		fun(nameByte, string(foundAt))
	}
}

func readConnsMap(peer *Peer, buf []byte, table map[PeerName]*Peer) map[PeerName]Connection {
	conns := make(map[PeerName]Connection)
	connsIterator(buf, func(nameByte []byte, remoteTCPAddr string) {
		name := PeerNameFromBin(nameByte)
		remotePeer := table[name]
		conn := NewRemoteConnection(peer, remotePeer, remoteTCPAddr)
		conns[name] = conn
	})
	return conns
}
<commit_msg>Show the IP address on the end of each connection<commit_after>package weave

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"io"
	"sync"
)

type PeerCache struct {
	sync.RWMutex
	table map[PeerName]*Peer
	onGC  func(*Peer)
}

func NewPeerCache(onGC func(*Peer)) *PeerCache {
	return &PeerCache{table: make(map[PeerName]*Peer), onGC: onGC}
}

func (cache *PeerCache) FetchWithDefault(peer *Peer) *Peer {
	cache.RLock()
	res, found := cache.fetchAlias(peer)
	cache.RUnlock()
	if found {
		return res
	}
	cache.Lock()
	defer cache.Unlock()
	res, found = cache.fetchAlias(peer)
	if found {
		return res
	}
	cache.table[peer.Name] = peer
	peer.IncrementLocalRefCount()
	return peer
}

func (cache *PeerCache) Fetch(name PeerName) (*Peer, bool) {
	cache.RLock()
	defer cache.RUnlock()
	peer, found := cache.table[name]
	return peer, found // GRRR, why can't I inline this!?
}

func (cache *PeerCache) ForEach(fun func(PeerName, *Peer)) {
	cache.RLock()
	defer cache.RUnlock()
	for name, peer := range cache.table {
		fun(name, peer)
	}
}

func (cache *PeerCache) ApplyUpdate(update []byte, router *Router) ([]byte, error) {
	cache.Lock()

	newPeers, decodedUpdate, decodedConns, err := cache.decodeUpdate(update, router)
	if err != nil {
		cache.Unlock()
		return nil, err
	}

	// By this point, we know the update doesn't refer to any peers we
	// have no knowledge of. We can now apply the update. Start by
	// adding in any new peers into the cache.
	for name, newPeer := range newPeers {
		cache.table[name] = newPeer
	}

	// Now apply the updates
	newUpdate := cache.applyUpdate(decodedUpdate, decodedConns, router)

	for _, peerRemoved := range cache.garbageCollect(router.Ourself) {
		delete(newUpdate, peerRemoved.Name)
	}

	// Don't need to hold cache lock any longer
	cache.Unlock()

	return encodePeersMap(newUpdate), nil
}

func (cache *PeerCache) EncodeAllPeers() []byte {
	cache.RLock()
	defer cache.RUnlock()
	return encodePeersMap(cache.table)
}

func EncodePeers(peers ...*Peer) []byte {
	buf := new(bytes.Buffer)
	enc := gob.NewEncoder(buf)
	for _, peer := range peers {
		peer.encodePeer(enc)
	}
	return buf.Bytes()
}

func (cache *PeerCache) GarbageCollect(router *Router) []*Peer {
	cache.Lock()
	defer cache.Unlock()
	return cache.garbageCollect(router.Ourself)
}

func (cache *PeerCache) String() string {
	var buf bytes.Buffer
	cache.ForEach(func(name PeerName, peer *Peer) {
		buf.WriteString(fmt.Sprint(peer, "\n"))
		peer.ForEachConnection(func(remoteName PeerName, conn Connection) {
			buf.WriteString(fmt.Sprintf("   -> %v [%v]\n", remoteName, conn.RemoteTCPAddr()))
		})
	})
	return buf.String()
}

func (cache *PeerCache) fetchAlias(peer *Peer) (*Peer, bool) {
	if existingPeer, found := cache.table[peer.Name]; found {
		if existingPeer.UID == peer.UID {
			existingPeer.IncrementLocalRefCount()
			return existingPeer, true
		} else {
			return nil, true
		}
	}
	return nil, false
}

func (cache *PeerCache) garbageCollect(ourself *Peer) []*Peer {
	removed := []*Peer{}
	for name, peer := range cache.table {
		found, _ := ourself.HasPathTo(peer, false)
		if !found && !peer.IsLocallyReferenced() {
			cache.onGC(peer)
			delete(cache.table, name)
			ourself.Router.Macs.Delete(peer)
			removed = append(removed, peer)
		}
	}
	return removed
}

func encodePeersMap(peers map[PeerName]*Peer) []byte {
	buf := new(bytes.Buffer)
	enc := gob.NewEncoder(buf)
	for _, peer := range peers {
		peer.encodePeer(enc)
	}
	return buf.Bytes()
}

func (cache *PeerCache) decodeUpdate(update []byte, router *Router) (newPeers map[PeerName]*Peer, decodedUpdate []*Peer, decodedConns [][]byte, err error) {
	newPeers = make(map[PeerName]*Peer)
	decodedUpdate = []*Peer{}
	decodedConns = [][]byte{}

	updateBuf := new(bytes.Buffer)
	updateBuf.Write(update)
	decoder := gob.NewDecoder(updateBuf)

	for {
		nameByte, uid, version, connsBuf, decErr := decodePeerNoConns(decoder)
		if decErr == io.EOF {
			break
		} else if decErr != nil {
			err = decErr
			return
		}
		name := PeerNameFromBin(nameByte)
		newPeer := NewPeer(name, uid, version, router)
		decodedUpdate = append(decodedUpdate, newPeer)
		decodedConns = append(decodedConns, connsBuf)
		existingPeer, found := cache.table[name]
		if !found {
			newPeers[name] = newPeer
		} else if existingPeer.UID != newPeer.UID {
			err = NameCollisionError{Name: newPeer.Name}
			return
		}
	}

	unknownPeers := false
	for _, connsBuf := range decodedConns {
		connsIterator(connsBuf, func(remoteNameByte []byte, _ string) {
			remoteName := PeerNameFromBin(remoteNameByte)
			if _, found := newPeers[remoteName]; found {
				return
			}
			if _, found := cache.table[remoteName]; found {
				return
			}
			// Update refers to a peer which we have no knowledge
			// of. Thus we can't apply the update. Abort.
			unknownPeers = true
		})
	}
	if unknownPeers {
		err = UnknownPeersError{}
		return
	}
	return
}

func (cache *PeerCache) applyUpdate(decodedUpdate []*Peer, decodedConns [][]byte, router *Router) map[PeerName]*Peer {
	newUpdate := make(map[PeerName]*Peer)
	for idx, newPeer := range decodedUpdate {
		connsBuf := decodedConns[idx]
		name := newPeer.Name
		// guaranteed to find peer in the cache.table
		peer := cache.table[name]
		if peer != newPeer {
			if peer.Version() > newPeer.Version() {
				// we know more about this one than the update. If
				// peer is ourself, this is slightly racey (further
				// changes could occur to ourself in the mean
				// time). But it doesn't matter as we know that we're
				// already > newPeer.Version() and that's not going to
				// change.
				newUpdate[name] = peer
				continue
			} else if peer == router.Ourself {
				// nobody but us updates us
				continue
			} else if peer.Version() == newPeer.Version() {
				// implication is that connections are equal too
				continue
			}
		}
		// If we're here, either it was a new peer, or the update has
		// more info about the peer than we do. Either case, we need
		// to set version and conns and include the updated peer in
		// the outgoing update.

		// Can peer have been updated by anyone else in the mean time?
		// No - we know that peer is not ourself, so the only prospect
		// for an update would be someone else calling
		// router.Peers.ApplyUpdate. But ApplyUpdate takes the Lock on
		// the peers cache, so there can be no race here.
		conns := readConnsMap(peer, connsBuf, cache.table)
		peer.SetVersionAndConnections(newPeer.Version(), conns)
		newUpdate[name] = peer
	}
	return newUpdate
}

func (peer *Peer) encodePeer(enc *gob.Encoder) {
	peer.RLock()
	defer peer.RUnlock()

	checkFatal(enc.Encode(peer.NameByte))
	checkFatal(enc.Encode(peer.UID))
	checkFatal(enc.Encode(peer.version))

	connsBuf := new(bytes.Buffer)
	connsEnc := gob.NewEncoder(connsBuf)
	for _, conn := range peer.connections {
		// DANGER holding rlock on peer, going to take rlock on conn
		if !conn.Established() {
			continue
		}
		checkFatal(connsEnc.Encode(conn.Remote().NameByte))
		checkFatal(connsEnc.Encode(conn.RemoteTCPAddr()))
	}
	checkFatal(enc.Encode(connsBuf.Bytes()))
}

func decodePeerNoConns(dec *gob.Decoder) (nameByte []byte, uid uint64, version uint64, conns []byte, err error) {
	err = dec.Decode(&nameByte)
	if err != nil {
		return
	}
	err = dec.Decode(&uid)
	if err != nil {
		return
	}
	err = dec.Decode(&version)
	if err != nil {
		return
	}
	err = dec.Decode(&conns)
	if err == io.EOF {
		err = nil
	}
	return
}

func connsIterator(input []byte, fun func([]byte, string)) {
	buf := new(bytes.Buffer)
	buf.Write(input)
	dec := gob.NewDecoder(buf)
	for {
		var nameByte []byte
		err := dec.Decode(&nameByte)
		if err == io.EOF {
			return
		}
		checkFatal(err)
		var foundAt string
		checkFatal(dec.Decode(&foundAt))
		fun(nameByte, string(foundAt))
	}
}

func readConnsMap(peer *Peer, buf []byte, table map[PeerName]*Peer) map[PeerName]Connection {
	conns := make(map[PeerName]Connection)
	connsIterator(buf, func(nameByte []byte, remoteTCPAddr string) {
		name := PeerNameFromBin(nameByte)
		remotePeer := table[name]
		conn := NewRemoteConnection(peer, remotePeer, remoteTCPAddr)
		conns[name] = conn
	})
	return conns
}
<|endoftext|>


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

package agent

import (
	"fmt"
	"os"
	"time"

	"github.com/gyuho/psn"
)

// startMetrics starts collecting metrics.
func startMetrics(fs *flags, t *transporterServer) error {
	if fs == nil || t == nil || t.cmd == nil {
		return fmt.Errorf("cannot find process to track (%+v, %+v)", fs, t)
	}
	plog.Infof("starting collecting metrics [database %q | PID: %d | disk device: %q | network interface: %q]",
		t.req.Database, t.pid, fs.diskDevice, fs.networkInterface)

	if err := os.RemoveAll(fs.systemMetricsLog); err != nil {
		return err
	}

	if err := toFile(fmt.Sprintf("%d", t.req.ClientNum), t.clientNumPath); err != nil {
		return err
	}
	c := psn.NewCSV(fs.systemMetricsLog, t.pid, fs.diskDevice, fs.networkInterface, t.clientNumPath)
	if err := c.Add(); err != nil {
		return err
	}
	t.metricsCSV = c

	go func() {
		for {
			select {
			case <-time.After(time.Second):
				if err := c.Add(); err != nil {
					plog.Errorf("psn.CSV.Add error (%v)", err)
					continue
				}

			case <-t.uploadSig:
				plog.Infof("upload signal received; saving CSV at %q", t.metricsCSV.FilePath)
				if err := t.metricsCSV.Save(); err != nil {
					plog.Errorf("psn.CSV.Save error %v", err)
				} else {
					plog.Infof("CSV saved at %q", t.metricsCSV.FilePath)
				}
				close(t.csvReady)
				return

			case sig := <-t.notifier:
				plog.Infof("signal received %q", sig.String())
				return
			}
		}
	}()
	return nil
}
<commit_msg>agent: clean up<commit_after>// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package agent

import (
	"fmt"
	"os"
	"time"

	"github.com/gyuho/psn"
)

// startMetrics starts collecting metrics.
func startMetrics(fs *flags, t *transporterServer) error {
	if fs == nil || t == nil || t.cmd == nil {
		return fmt.Errorf("cannot find process to track (%+v, %+v)", fs, t)
	}
	plog.Infof("starting collecting metrics [database %q | PID: %d | disk device: %q | network interface: %q]",
		t.req.Database, t.pid, fs.diskDevice, fs.networkInterface)

	if err := os.RemoveAll(fs.systemMetricsLog); err != nil {
		return err
	}
	if err := toFile(fmt.Sprintf("%d", t.req.ClientNum), t.clientNumPath); err != nil {
		return err
	}
	t.metricsCSV = psn.NewCSV(fs.systemMetricsLog, t.pid, fs.diskDevice, fs.networkInterface, t.clientNumPath)
	if err := t.metricsCSV.Add(); err != nil {
		return err
	}

	go func() {
		for {
			select {
			case <-time.After(time.Second):
				if err := t.metricsCSV.Add(); err != nil {
					plog.Errorf("psn.CSV.Add error (%v)", err)
					continue
				}

			case <-t.uploadSig:
				plog.Infof("upload signal received; saving CSV at %q", t.metricsCSV.FilePath)
				if err := t.metricsCSV.Save(); err != nil {
					plog.Errorf("psn.CSV.Save error %v", err)
				} else {
					plog.Infof("CSV saved at %q", t.metricsCSV.FilePath)
				}
				close(t.csvReady)
				return

			case sig := <-t.notifier:
				plog.Infof("signal received %q", sig.String())
				return
			}
		}
	}()
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2013 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 ssa_test

import (
	"fmt"
	"go/build"
	"go/parser"
	"os"

	"code.google.com/p/go.tools/importer"
	"code.google.com/p/go.tools/ssa"
)

// This program demonstrates how to run the SSA builder on a "Hello,
// World!" program and shows the printed representation of packages,
// functions and instructions.
//
// Within the function listing, the name of each BasicBlock such as
// ".0.entry" is printed left-aligned, followed by the block's
// Instructions.
//
// For each instruction that defines an SSA virtual register
// (i.e. implements Value), the type of that value is shown in the
// right column.
//
// Build and run the ssadump.go program in this package if you want a
// standalone tool with similar functionality.
//
func Example() {
	const hello = `
package main

import "fmt"

const message = "Hello, World!"

func main() {
	fmt.Println(message)
}
`
	// Construct an importer.  Imports will be loaded as if by 'go build'.
	imp := importer.New(&importer.Config{Build: &build.Default})

	// Parse the input file.
	file, err := parser.ParseFile(imp.Fset, "hello.go", hello, 0)
	if err != nil {
		fmt.Print(err) // parse error
		return
	}

	// Create single-file main package and import its dependencies.
	mainInfo := imp.CreatePackage("main", file)

	// Create SSA-form program representation.
	var mode ssa.BuilderMode
	prog := ssa.NewProgram(imp.Fset, mode)
	if err := prog.CreatePackages(imp); err != nil {
		fmt.Print(err) // type error in some package
		return
	}
	mainPkg := prog.Package(mainInfo.Pkg)

	// Print out the package.
	mainPkg.DumpTo(os.Stdout)

	// Build SSA code for bodies of functions in mainPkg.
	mainPkg.Build()

	// Print out the package-level functions.
	mainPkg.Func("init").DumpTo(os.Stdout)
	mainPkg.Func("main").DumpTo(os.Stdout)

	// Output:
	//
	// package main:
	//   func  init       func()
	//   var   init$guard bool
	//   func  main       func()
	//   const message    message = "Hello, World!":untyped string
	//
	// # Name: main.init
	// # Package: main
	// # Synthetic: package initializer
	// func init():
	// .0.entry:                                                               P:0 S:2
	// 	t0 = *init$guard                                                   bool
	// 	if t0 goto 2.init.done else 1.init.start
	// .1.init.start:                                                          P:1 S:1
	// 	*init$guard = true:bool
	// 	t1 = fmt.init()                                                      ()
	// 	jump 2.init.done
	// .2.init.done:                                                           P:2 S:0
	// 	return
	//
	// # Name: main.main
	// # Package: main
	// # Location: hello.go:8:6
	// func main():
	// .0.entry:                                                               P:0 S:0
	// 	t0 = new [1]interface{} (varargs)                       *[1]interface{}
	// 	t1 = &t0[0:int]                                            *interface{}
	// 	t2 = make interface{} <- string ("Hello, World!":string)    interface{}
	// 	*t1 = t2
	// 	t3 = slice t0[:]                                          []interface{}
	// 	t4 = fmt.Println(t3)                                 (n int, err error)
	// 	return
}
<commit_msg>ssa: Update the location of the ssadump command in an example comment.<commit_after>// Copyright 2013 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 ssa_test

import (
	"fmt"
	"go/build"
	"go/parser"
	"os"

	"code.google.com/p/go.tools/importer"
	"code.google.com/p/go.tools/ssa"
)

// This program demonstrates how to run the SSA builder on a "Hello,
// World!" program and shows the printed representation of packages,
// functions and instructions.
//
// Within the function listing, the name of each BasicBlock such as
// ".0.entry" is printed left-aligned, followed by the block's
// Instructions.
//
// For each instruction that defines an SSA virtual register
// (i.e. implements Value), the type of that value is shown in the
// right column.
//
// Build and run the ssadump.go program if you want a standalone tool
// with similar functionality. It is located at
// code.google.com/p/go.tools/cmd/ssadump.
//
func Example() {
	const hello = `
package main

import "fmt"

const message = "Hello, World!"

func main() {
	fmt.Println(message)
}
`
	// Construct an importer.  Imports will be loaded as if by 'go build'.
	imp := importer.New(&importer.Config{Build: &build.Default})

	// Parse the input file.
	file, err := parser.ParseFile(imp.Fset, "hello.go", hello, 0)
	if err != nil {
		fmt.Print(err) // parse error
		return
	}

	// Create single-file main package and import its dependencies.
	mainInfo := imp.CreatePackage("main", file)

	// Create SSA-form program representation.
	var mode ssa.BuilderMode
	prog := ssa.NewProgram(imp.Fset, mode)
	if err := prog.CreatePackages(imp); err != nil {
		fmt.Print(err) // type error in some package
		return
	}
	mainPkg := prog.Package(mainInfo.Pkg)

	// Print out the package.
	mainPkg.DumpTo(os.Stdout)

	// Build SSA code for bodies of functions in mainPkg.
	mainPkg.Build()

	// Print out the package-level functions.
	mainPkg.Func("init").DumpTo(os.Stdout)
	mainPkg.Func("main").DumpTo(os.Stdout)

	// Output:
	//
	// package main:
	//   func  init       func()
	//   var   init$guard bool
	//   func  main       func()
	//   const message    message = "Hello, World!":untyped string
	//
	// # Name: main.init
	// # Package: main
	// # Synthetic: package initializer
	// func init():
	// .0.entry:                                                               P:0 S:2
	// 	t0 = *init$guard                                                   bool
	// 	if t0 goto 2.init.done else 1.init.start
	// .1.init.start:                                                          P:1 S:1
	// 	*init$guard = true:bool
	// 	t1 = fmt.init()                                                      ()
	// 	jump 2.init.done
	// .2.init.done:                                                           P:2 S:0
	// 	return
	//
	// # Name: main.main
	// # Package: main
	// # Location: hello.go:8:6
	// func main():
	// .0.entry:                                                               P:0 S:0
	// 	t0 = new [1]interface{} (varargs)                       *[1]interface{}
	// 	t1 = &t0[0:int]                                            *interface{}
	// 	t2 = make interface{} <- string ("Hello, World!":string)    interface{}
	// 	*t1 = t2
	// 	t3 = slice t0[:]                                          []interface{}
	// 	t4 = fmt.Println(t3)                                 (n int, err error)
	// 	return
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"time"

	"github.com/davecheney/gpio/rpi"
	"github.com/paypal/gatt"
)

var (
	// services
	serviceUUID = gatt.MustParseUUID("54a64ddf-c756-4a1a-bf9d-14f2cac357ad")

	// characteristics
	carUUID  = gatt.MustParseUUID("fd1c6fcc-3ca5-48a9-97e9-37f81f5bd9c5")
	authUUID = gatt.MustParseUUID("66e01614-13d1-40d6-a34f-c5360ba57698")

	// beacons
	beaconUUID = gatt.MustParseUUID("a78d9129-b79a-400f-825e-b691661123eb")
)

type PeripheralConfig struct {
	Secret []byte
	Public bool
}

type Peripheral struct {
	Beacon *gatt.Server
	Server *gatt.Server

	Car            *Car
	Auth           *Auth
	PiLED          *StatusLED
	BluetoothLED   *StatusLED
	PowerSavingLED *StatusLED
	AccPowerSensor *Sensor
	DoorLockSensor *Sensor
}

func NewPeripheral(c *PeripheralConfig) *Peripheral {
	p := new(Peripheral)
	p.Init(c)
	return p
}

// main starts up the BLE server
func (p *Peripheral) Init(c *PeripheralConfig) {

	p.Beacon = gatt.NewServer(
		gatt.Name("KhakiBeacon"),
		gatt.HCI(1),
		gatt.AdvertisingPacket(iBeaconPacket(&iBeaconConfig{
			UUID:  beaconUUID,
			Major: 0,
			Minor: 0,
			Power: 0xCD,
		})),
	)

	p.Server = gatt.NewServer(
		gatt.Name("Khaki"),
		gatt.HCI(0),
		gatt.Connect(p.handleConnect),
		gatt.Disconnect(p.handleDisconnect),
	)
	service := p.Server.AddService(serviceUUID)

	// Pi LED
	gpioPin17, err := OpenPinForInput(rpi.GPIO23)
	if err != nil {
		log.Println("Could not open GPIO pin 17")
	}

	// Power Saving LED
	gpioPin18, err := OpenPinForInput(rpi.GPIO23)
	if err != nil {
		log.Println("Could not open GPIO pin 18")
	}

	// Acc Power Sensor
	gpioPin23, err := OpenPinForInput(rpi.GPIO23)
	if err != nil {
		log.Println("Could not open GPIO pin 23")
	}

	// Bluetooth LED
	gpioPin24, err := OpenPinForOutput(rpi.GPIO24)
	if err != nil {
		log.Println("Could not open GPIO pin 24")
	}

	// Remote Relay
	gpioPin25, err := OpenPinForOutput(rpi.GPIO25)
	if err != nil {
		log.Println("Could not open GPIO pin 25")
	}

	p.PiLED = &StatusLED{
		Pin:   gpioPin17,
		Blink: 2,
	}

	p.PiLED.Update(true)

	p.BluetoothLED = &StatusLED{
		Pin:   gpioPin24,
		Blink: 0,
	}

	p.PowerSavingLED = &StatusLED{
		Pin:   gpioPin18,
		Blink: 0,
	}

	// create auth instance
	p.Auth = NewAuth(c.Secret, c.Public)

	// auth characteristic
	authChar := service.AddCharacteristic(authUUID)
	authChar.HandleReadFunc(p.Auth.HandleRead)
	authChar.HandleWriteFunc(p.Auth.HandleWrite)

	// create car instance
	p.Car = NewCar(gpioPin25, p.Auth)

	// car characteristic
	carChar := service.AddCharacteristic(carUUID)
	carChar.HandleReadFunc(p.Car.HandleRead)
	carChar.HandleWriteFunc(p.Car.HandleWrite)
	carChar.HandleNotifyFunc(p.Car.HandleNotify)

	// sensor
	p.AccPowerSensor = &Sensor{
		Pin: gpioPin23,
		HandleChange: func(sensor bool) {
			p.Car.ToggleNotifications(sensor)
			p.PowerSavingLED.Update(sensor)
		},
	}
	p.AccPowerSensor.Init()
}

// Start starts running the BLE servers
func (p *Peripheral) Start() {
	go func() {
		err := p.Beacon.AdvertiseAndServe()
		if err != nil {
			log.Printf("Error with iBeacon: %s\n", err)
		}
	}()

	go func() {
		log.Fatal(p.Server.AdvertiseAndServe())
	}()

	select {}
}

// HandleConnect is called when a central connects
func (p *Peripheral) handleConnect(conn gatt.Conn) {
	log.Println("Got connection", conn)
	p.BluetoothLED.Update(true)

	log.Println("You have 5 seconds...")

	go func() {
		time.Sleep(5 * time.Second)
		if !p.Auth.IsAuthenticated() {
			log.Println("You have been disconnected")
			conn.Close()
		}
	}()
}

// HandleDisconnect is called when a connection is lost
func (p *Peripheral) handleDisconnect(conn gatt.Conn) {
	log.Println("Lost connection", conn)
	p.Car.Lock()
	p.Car.Reset()
	p.Auth.Invalidate()
	p.BluetoothLED.Update(false)
}
<commit_msg>Maybe it will help if I commit the code<commit_after>package main

import (
	"log"
	"time"

	"github.com/davecheney/gpio/rpi"
	"github.com/paypal/gatt"
)

var (
	// services
	serviceUUID = gatt.MustParseUUID("54a64ddf-c756-4a1a-bf9d-14f2cac357ad")

	// characteristics
	carUUID  = gatt.MustParseUUID("fd1c6fcc-3ca5-48a9-97e9-37f81f5bd9c5")
	authUUID = gatt.MustParseUUID("66e01614-13d1-40d6-a34f-c5360ba57698")

	// beacons
	beaconUUID = gatt.MustParseUUID("a78d9129-b79a-400f-825e-b691661123eb")
)

type PeripheralConfig struct {
	Secret []byte
	Public bool
}

type Peripheral struct {
	Beacon *gatt.Server
	Server *gatt.Server

	Car            *Car
	Auth           *Auth
	PiLED          *StatusLED
	BluetoothLED   *StatusLED
	PowerSavingLED *StatusLED
	AccPowerSensor *Sensor
	DoorLockSensor *Sensor
}

func NewPeripheral(c *PeripheralConfig) *Peripheral {
	p := new(Peripheral)
	p.Init(c)
	return p
}

// main starts up the BLE server
func (p *Peripheral) Init(c *PeripheralConfig) {

	p.Beacon = gatt.NewServer(
		gatt.Name("KhakiBeacon"),
		gatt.HCI(1),
		gatt.AdvertisingPacket(iBeaconPacket(&iBeaconConfig{
			UUID:  beaconUUID,
			Major: 0,
			Minor: 0,
			Power: 0xCD,
		})),
	)

	p.Server = gatt.NewServer(
		gatt.Name("Khaki"),
		gatt.HCI(0),
		gatt.Connect(p.handleConnect),
		gatt.Disconnect(p.handleDisconnect),
	)
	service := p.Server.AddService(serviceUUID)

	// Pi LED
	gpioPin17, err := OpenPinForInput(rpi.GPIO17)
	if err != nil {
		log.Println("Could not open GPIO pin 17")
	}

	// Power Saving LED
	gpioPin18, err := OpenPinForInput(rpi.GPIO18)
	if err != nil {
		log.Println("Could not open GPIO pin 18")
	}

	// Acc Power Sensor
	gpioPin23, err := OpenPinForInput(rpi.GPIO23)
	if err != nil {
		log.Println("Could not open GPIO pin 23")
	}

	// Bluetooth LED
	gpioPin24, err := OpenPinForOutput(rpi.GPIO24)
	if err != nil {
		log.Println("Could not open GPIO pin 24")
	}

	// Remote Relay
	gpioPin25, err := OpenPinForOutput(rpi.GPIO25)
	if err != nil {
		log.Println("Could not open GPIO pin 25")
	}

	p.PiLED = &StatusLED{
		Pin:   gpioPin17,
		Blink: 2,
	}

	log.Println("LED Should be turning on maybe?")
	p.PiLED.Update(true)

	p.BluetoothLED = &StatusLED{
		Pin:   gpioPin24,
		Blink: 0,
	}

	p.PowerSavingLED = &StatusLED{
		Pin:   gpioPin18,
		Blink: 0,
	}

	// create auth instance
	p.Auth = NewAuth(c.Secret, c.Public)

	// auth characteristic
	authChar := service.AddCharacteristic(authUUID)
	authChar.HandleReadFunc(p.Auth.HandleRead)
	authChar.HandleWriteFunc(p.Auth.HandleWrite)

	// create car instance
	p.Car = NewCar(gpioPin25, p.Auth)

	// car characteristic
	carChar := service.AddCharacteristic(carUUID)
	carChar.HandleReadFunc(p.Car.HandleRead)
	carChar.HandleWriteFunc(p.Car.HandleWrite)
	carChar.HandleNotifyFunc(p.Car.HandleNotify)

	// sensor
	p.AccPowerSensor = &Sensor{
		Pin: gpioPin23,
		HandleChange: func(sensor bool) {
			p.Car.ToggleNotifications(sensor)
			p.PowerSavingLED.Update(sensor)
		},
	}
	p.AccPowerSensor.Init()
}

// Start starts running the BLE servers
func (p *Peripheral) Start() {
	go func() {
		err := p.Beacon.AdvertiseAndServe()
		if err != nil {
			log.Printf("Error with iBeacon: %s\n", err)
		}
	}()

	go func() {
		log.Fatal(p.Server.AdvertiseAndServe())
	}()

	select {}
}

// HandleConnect is called when a central connects
func (p *Peripheral) handleConnect(conn gatt.Conn) {
	log.Println("Got connection", conn)
	p.BluetoothLED.Update(true)

	log.Println("You have 5 seconds...")

	go func() {
		time.Sleep(5 * time.Second)
		if !p.Auth.IsAuthenticated() {
			log.Println("You have been disconnected")
			conn.Close()
		}
	}()
}

// HandleDisconnect is called when a connection is lost
func (p *Peripheral) handleDisconnect(conn gatt.Conn) {
	log.Println("Lost connection", conn)
	p.Car.Lock()
	p.Car.Reset()
	p.Auth.Invalidate()
	p.BluetoothLED.Update(false)
}
<|endoftext|>


<commit_before>package sohop

import (
	"fmt"
	"math/rand"
	"net/http"
	"time"

	"bitbucket.org/davars/sohop/auth"
	"github.com/gorilla/mux"
	"github.com/gorilla/securecookie"
	"github.com/gorilla/sessions"
)

type Config struct {
	Domain          string
	Backends        map[string]backend
	GithubAPI       oauthApp
	AuthorizedOrgID int
}

type backend struct {
	URL         string
	Auth        bool
	HealthCheck string
	WebSocket   string
}

type oauthApp struct {
	ClientID     string
	ClientSecret string
}

var (
	store       = sessions.NewCookieStore(securecookie.GenerateRandomKey(64))
	sessionName = sessionID()
	sessionAge  = 24 * time.Hour
)

func sessionID() string {
	return fmt.Sprintf("_s%d", rand.Uint32())
}

func (c *Config) authorizer() auth.Authorizer {
	return auth.GithubAuth{
		ClientID:     c.GithubAPI.ClientID,
		ClientSecret: c.GithubAPI.ClientSecret,
		OrgID:        c.AuthorizedOrgID,
	}
}

func Handler(conf *Config) (http.Handler, error) {
	store.Options.HttpOnly = true
	store.Options.Secure = true
	store.Options.Domain = conf.Domain
	store.Options.MaxAge = int(sessionAge / time.Second)

	router := mux.NewRouter()
	router.NotFoundHandler = http.HandlerFunc(notFound)

	router.
		Host(fmt.Sprintf("oauth.%s", conf.Domain)).
		Path("/authorized").
		Handler(
		auth.Handler(store, sessionName, conf.authorizer()))

	healthRouter := router.Host(fmt.Sprintf("health.%s", conf.Domain)).Subrouter()
	healthRouter.Path("/check").Handler(health(conf))

	proxyRouter := router.Host(fmt.Sprintf("{subdomain:[a-z]+}.%s", conf.Domain)).Subrouter()
	authentication := auth.Middleware(store, sessionName, conf.authorizer())
	proxy, err := conf.ProxyHandler()
	if err != nil {
		return nil, err
	}

	proxyRouter.MatcherFunc(requiresAuth(conf)).Handler(authentication(proxy))
	proxyRouter.PathPrefix("/").Handler(proxy)

	return logging(router), nil
}
<commit_msg>Note to self: math/rand requires seeding.  Makes sense for simulations, I guess.  Switch to crypto/rand.<commit_after>package sohop

import (
	"crypto/rand"
	"fmt"
	"log"
	"math"
	"math/big"
	"net/http"
	"time"

	"bitbucket.org/davars/sohop/auth"
	"github.com/gorilla/mux"
	"github.com/gorilla/securecookie"
	"github.com/gorilla/sessions"
)

type Config struct {
	Domain          string
	Backends        map[string]backend
	GithubAPI       oauthApp
	AuthorizedOrgID int
}

type backend struct {
	URL         string
	Auth        bool
	HealthCheck string
	WebSocket   string
}

type oauthApp struct {
	ClientID     string
	ClientSecret string
}

var (
	store       = sessions.NewCookieStore(securecookie.GenerateRandomKey(64))
	sessionName = sessionID()
	sessionAge  = 24 * time.Hour
)

func sessionID() string {
	n, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
	if err != nil {
		log.Fatal(err)
	}
	id := fmt.Sprintf("_s%d", n)
	return id
}

func (c *Config) authorizer() auth.Authorizer {
	return auth.GithubAuth{
		ClientID:     c.GithubAPI.ClientID,
		ClientSecret: c.GithubAPI.ClientSecret,
		OrgID:        c.AuthorizedOrgID,
	}
}

func Handler(conf *Config) (http.Handler, error) {
	store.Options.HttpOnly = true
	store.Options.Secure = true
	store.Options.Domain = conf.Domain
	store.Options.MaxAge = int(sessionAge / time.Second)

	router := mux.NewRouter()
	router.NotFoundHandler = http.HandlerFunc(notFound)

	router.
		Host(fmt.Sprintf("oauth.%s", conf.Domain)).
		Path("/authorized").
		Handler(
		auth.Handler(store, sessionName, conf.authorizer()))

	healthRouter := router.Host(fmt.Sprintf("health.%s", conf.Domain)).Subrouter()
	healthRouter.Path("/check").Handler(health(conf))

	proxyRouter := router.Host(fmt.Sprintf("{subdomain:[a-z]+}.%s", conf.Domain)).Subrouter()
	authentication := auth.Middleware(store, sessionName, conf.authorizer())
	proxy, err := conf.ProxyHandler()
	if err != nil {
		return nil, err
	}

	proxyRouter.MatcherFunc(requiresAuth(conf)).Handler(authentication(proxy))
	proxyRouter.PathPrefix("/").Handler(proxy)

	return logging(router), nil
}
<|endoftext|>


<commit_before>package message

import (
	"github.com/thoj/go-ircevent"
	"time"
)

type InboundMsg struct {
	Msg     string
	MsgArgs []string
	Src     string
	Event   *irc.Event
}

type OutboundMsg struct {
	Msg  string
	Dest string
	Conn *irc.Connection
}

func SayLoop(outChan chan OutboundMsg) {
	messageTimeouts := make(map[string]time.Time)

	for o := range outChan {
		sleepDuration := time.Duration(0)

		if prevTime, ok := messageTimeouts[o.Dest]; ok {
			sleepDuration = time.Second - time.Now().Sub(prevTime)
			if sleepDuration < 0 {
				sleepDuration = time.Duration(0)
			}
		}

		time.Sleep(sleepDuration)
		messageTimeouts[o.Dest] = time.Now()

		o.Conn.Privmsg(o.Dest, o.Msg)
	}
}
<commit_msg>fixed message rate-limiting to work per-channel rather than globally<commit_after>package message

import (
	"github.com/thoj/go-ircevent"
	"time"
)

type InboundMsg struct {
	Msg     string
	MsgArgs []string
	Src     string
	Event   *irc.Event
}

type OutboundMsg struct {
	Msg  string
	Dest string
	Conn *irc.Connection
}

func SayLoop(outChan chan OutboundMsg) {
	latestMessageTimes := make(map[string]time.Time)

	for o := range outChan {
		timerDuration := time.Duration(0)
		inboundTime := time.Now()

		if lastMessageTime, ok := latestMessageTimes[o.Dest]; ok {
			timerDuration = time.Second - inboundTime.Sub(lastMessageTime)
			if timerDuration < 0 {
				timerDuration = time.Duration(0)
			}
		}

		latestMessageTimes[o.Dest] = inboundTime.Add(timerDuration)
		conn := o.Conn
		dest := o.Dest
		msg := o.Msg
		time.AfterFunc(timerDuration, func() {
			conn.Privmsg(dest, msg)
		})
	}
}
<|endoftext|>