<commit_before>package main

import (
	"encoding/json"
	"github.com/stretchr/testify/assert"
	"github.com/stripe/sequins/backend"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

func getSequins(t *testing.T, opts sequinsOptions) *sequins {
	backend := backend.NewLocalBackend("test_data")
	s := newSequins(backend, opts)

	go func() {
		err := s.start("localhost:0")
		if err != nil {
			t.Fatal(err)
		}
	}()

	time.Sleep(100 * time.Millisecond)
	return s
}

func TestSequins(t *testing.T) {
	ts := getSequins(t, sequinsOptions{"test_data", false})

	req, _ := http.NewRequest("GET", "/Alice", nil)
	w := httptest.NewRecorder()
	ts.ServeHTTP(w, req)

	assert.Equal(t, 200, w.Code)
	assert.Equal(t, "Practice", w.Body.String(), "Practice")

	req, _ = http.NewRequest("GET", "/foo", nil)
	w = httptest.NewRecorder()
	ts.ServeHTTP(w, req)

	assert.Equal(t, 404, w.Code)
	assert.Equal(t, "", w.Body.String())

	req, _ = http.NewRequest("GET", "/", nil)
	w = httptest.NewRecorder()
	ts.ServeHTTP(w, req)

	now := time.Now().Unix() - 1
	status := &status{}
	err := json.Unmarshal(w.Body.Bytes(), status)
	require.NoError(t, err)
	assert.Equal(t, 200, w.Code, 200)
	assert.Equal(t, "test_data/1", status.Path)
	assert.True(t, status.Started >= now)
	assert.Equal(t, 3, status.Count)
}

func TestSequinsNoValidDirectories(t *testing.T) {
	backend := backend.NewLocalBackend("test_data/0")
	s := newSequins(backend, sequinsOptions{"test_data/0", false})
	err := s.start("localhost:0")
	assert.Error(t, err)
}
<commit_msg>missing import<commit_after>package main

import (
	"encoding/json"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/stripe/sequins/backend"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

func getSequins(t *testing.T, opts sequinsOptions) *sequins {
	backend := backend.NewLocalBackend("test_data")
	s := newSequins(backend, opts)

	go func() {
		err := s.start("localhost:0")
		if err != nil {
			t.Fatal(err)
		}
	}()

	time.Sleep(100 * time.Millisecond)
	return s
}

func TestSequins(t *testing.T) {
	ts := getSequins(t, sequinsOptions{"test_data", false})

	req, _ := http.NewRequest("GET", "/Alice", nil)
	w := httptest.NewRecorder()
	ts.ServeHTTP(w, req)

	assert.Equal(t, 200, w.Code)
	assert.Equal(t, "Practice", w.Body.String(), "Practice")

	req, _ = http.NewRequest("GET", "/foo", nil)
	w = httptest.NewRecorder()
	ts.ServeHTTP(w, req)

	assert.Equal(t, 404, w.Code)
	assert.Equal(t, "", w.Body.String())

	req, _ = http.NewRequest("GET", "/", nil)
	w = httptest.NewRecorder()
	ts.ServeHTTP(w, req)

	now := time.Now().Unix() - 1
	status := &status{}
	err := json.Unmarshal(w.Body.Bytes(), status)
	require.NoError(t, err)
	assert.Equal(t, 200, w.Code, 200)
	assert.Equal(t, "test_data/1", status.Path)
	assert.True(t, status.Started >= now)
	assert.Equal(t, 3, status.Count)
}

func TestSequinsNoValidDirectories(t *testing.T) {
	backend := backend.NewLocalBackend("test_data/0")
	s := newSequins(backend, sequinsOptions{"test_data/0", false})
	err := s.start("localhost:0")
	assert.Error(t, err)
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 registry

// Registry abstracts a types registry which holds types that can be
// used in a Deployment Manager configurations. A registry root must have
// a 'types' directory which contains all the available types. Each type
// then contains version directory which in turn contains all the files
// necessary for that type.
// For example a type registry holding two types:
//   redis v1               (implemented in jinja)
//   replicatedservice v2   (implemented in python)
// would have a directory structure like so:
// /types/redis/v1
//   redis.jinja
//   redis.jinja.schema
// /types/replicatedservice/v2
//   replicatedservice.python
//   replicatedservice.python.schema

const TypesDir string = "types"

type Type struct {
	Name string
	Version string
}

// Registry abstracts type interactions.
type Registry interface {
	// List all the types in the given registry
	List() ([]Type, error)
	// Get the download URL for a given type and version
	GetURL(t Type) (string, error)
}
<commit_msg>fix typo directory->directories<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 registry

// Registry abstracts a types registry which holds types that can be
// used in a Deployment Manager configurations. A registry root must have
// a 'types' directory which contains all the available types. Each type
// then contains version directories which in turn contains all the files
// necessary for that type.
// For example a type registry holding two types:
//   redis v1               (implemented in jinja)
//   replicatedservice v2   (implemented in python)
// would have a directory structure like so:
// /types/redis/v1
//   redis.jinja
//   redis.jinja.schema
// /types/replicatedservice/v2
//   replicatedservice.python
//   replicatedservice.python.schema

const TypesDir string = "types"

type Type struct {
	Name string
	Version string
}

// Registry abstracts type interactions.
type Registry interface {
	// List all the types in the given registry
	List() ([]Type, error)
	// Get the download URL for a given type and version
	GetURL(t Type) (string, error)
}
<|endoftext|>


<commit_before>package libkbfs

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/syndtr/goleveldb/leveldb"
)

type blockRefLocalStatus int

const (
	noBlockRef blockRefLocalStatus = iota
	blockRef
	archivedBlockRef
)

type blockEntry struct {
	// These fields are only exported for serialization purposes.
	BlockData     []byte
	Refs          map[BlockRefNonce]blockRefLocalStatus
	KeyServerHalf BlockCryptKeyServerHalf
	Tlf           TlfID
}

// bserverLocalStorage abstracts the various methods of storing blocks
// for bserverLocal.
type bserverLocalStorage interface {
	get(id BlockID) (blockEntry, error)
	getAll(tlf TlfID) (map[BlockID]map[BlockRefNonce]bool, error)
	put(id BlockID, entry blockEntry) error
	addReference(id BlockID, refNonce BlockRefNonce) error
	removeReference(id BlockID, refNonce BlockRefNonce) error
	archiveReference(id BlockID, refNonce BlockRefNonce) error
	shutdown()
}

// bserverMemStorage stores block data in an in-memory map.
type bserverMemStorage struct {
	lock sync.RWMutex
	m    map[BlockID]blockEntry
}

var _ bserverLocalStorage = (*bserverMemStorage)(nil)

func makeBserverMemStorage() *bserverMemStorage {
	return &bserverMemStorage{m: make(map[BlockID]blockEntry)}
}

func (s *bserverMemStorage) get(id BlockID) (blockEntry, error) {
	s.lock.RLock()
	defer s.lock.RUnlock()

	entry, ok := s.m[id]
	if !ok {
		return blockEntry{}, BServerErrorBlockNonExistent{}
	}
	return entry, nil
}

func (s *bserverMemStorage) getAll(tlf TlfID) (
	map[BlockID]map[BlockRefNonce]bool, error) {
	res := make(map[BlockID]map[BlockRefNonce]bool)
	s.lock.RLock()
	defer s.lock.RUnlock()

	for id, entry := range s.m {
		if entry.Tlf != tlf {
			continue
		}
		res[id] = make(map[BlockRefNonce]bool)
		for ref := range entry.Refs {
			res[id][ref] = true
		}
	}
	return res, nil
}

func (s *bserverMemStorage) put(id BlockID, entry blockEntry) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	s.m[id] = entry
	return nil
}

func (s *bserverMemStorage) addReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	entry, ok := s.m[id]
	if !ok {
		return IncrementMissingBlockError{id}
	}

	// only add it if there's a non-archived reference
	for _, status := range entry.Refs {
		if status == blockRef {
			entry.Refs[refNonce] = blockRef
			s.m[id] = entry
			return nil
		}
	}
	return BServerErrorBlockArchived{""}
}

func (s *bserverMemStorage) removeReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	entry, ok := s.m[id]
	if !ok {
		// This block is already gone; no error.
		return nil
	}

	delete(entry.Refs, refNonce)
	if len(entry.Refs) == 0 {
		delete(s.m, id)
	} else {
		s.m[id] = entry
	}
	return nil
}

func (s *bserverMemStorage) archiveReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	entry, ok := s.m[id]
	if !ok {
		return ArchiveMissingBlockError{id, refNonce}
	}

	entry.Refs[refNonce] = archivedBlockRef
	s.m[id] = entry
	return nil
}

func (s *bserverMemStorage) shutdown() {
	// Nothing to do.
}

// bserverFileStorage stores block data in flat files on disk.
type bserverFileStorage struct {
	codec Codec
	lock  sync.RWMutex
	dir   string
}

var _ bserverLocalStorage = (*bserverFileStorage)(nil)

func makeBserverFileStorage(codec Codec, dir string) *bserverFileStorage {
	return &bserverFileStorage{codec: codec, dir: dir}
}

// Store each block in its own file with name equal to the hex-encoded
// blockID. Splay the filenames over 256^2 subdirectories (one byte
// for the hash type plus the first byte of the hash data) using the
// first four characters of the name to keep the number of directories
// in dir itself to a manageable number, similar to git.
func (s *bserverFileStorage) buildPath(id BlockID) string {
	idStr := id.String()
	return filepath.Join(s.dir, idStr[:4], idStr[4:])
}

func (s *bserverFileStorage) getLocked(p string) (blockEntry, error) {
	buf, err := ioutil.ReadFile(p)
	if err != nil {
		return blockEntry{}, err
	}

	var entry blockEntry
	err = s.codec.Decode(buf, &entry)
	if err != nil {
		return blockEntry{}, err
	}

	return entry, nil
}

func (s *bserverFileStorage) get(id BlockID) (blockEntry, error) {
	s.lock.RLock()
	defer s.lock.RUnlock()
	entry, err := s.getLocked(s.buildPath(id))
	if err != nil {
		if os.IsNotExist(err) {
			err = BServerErrorBlockNonExistent{}
		}
		return blockEntry{}, err
	}
	return entry, nil
}

func (s *bserverFileStorage) getAll(tlf TlfID) (
	map[BlockID]map[BlockRefNonce]bool, error) {
	return nil, errors.New("getAll not yet implemented for bserverFileStorage")
}

func (s *bserverFileStorage) putLocked(p string, entry blockEntry) error {
	entryBuf, err := s.codec.Encode(entry)
	if err != nil {
		return err
	}

	err = os.MkdirAll(filepath.Dir(p), 0700)
	if err != nil {
		return err
	}

	return ioutil.WriteFile(p, entryBuf, 0600)
}

func (s *bserverFileStorage) put(id BlockID, entry blockEntry) error {
	s.lock.Lock()
	defer s.lock.Unlock()
	return s.putLocked(s.buildPath(id), entry)
}

func (s *bserverFileStorage) addReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	p := s.buildPath(id)
	entry, err := s.getLocked(p)
	if err != nil {
		if os.IsNotExist(err) {
			return IncrementMissingBlockError{id}
		}
		return err
	}

	// only add it if there's a non-archived reference
	for _, status := range entry.Refs {
		if status == blockRef {
			entry.Refs[refNonce] = blockRef
			return s.putLocked(p, entry)
		}
	}
	return BServerErrorBlockArchived{""}
}

func (s *bserverFileStorage) removeReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	p := s.buildPath(id)
	entry, err := s.getLocked(p)
	if err != nil {
		if os.IsNotExist(err) {
			// This block is already gone; no error.
			return nil
		}
		return err
	}

	delete(entry.Refs, refNonce)
	if len(entry.Refs) == 0 {
		return os.Remove(p)
	}
	return s.putLocked(p, entry)
}

func (s *bserverFileStorage) archiveReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	p := s.buildPath(id)
	entry, err := s.getLocked(p)
	if err != nil {
		if os.IsNotExist(err) {
			return ArchiveMissingBlockError{id, refNonce}
		}
		return err
	}

	entry.Refs[refNonce] = archivedBlockRef
	return s.putLocked(p, entry)
}

func (s *bserverFileStorage) shutdown() {
	// Nothing to do.
}

// bserverLeveldbStorage stores block data in a LevelDB database. This
// is kept around only for benchmarking purposes.
type bserverLeveldbStorage struct {
	codec Codec
	lock  sync.RWMutex
	db    *leveldb.DB
}

var _ bserverLocalStorage = (*bserverLeveldbStorage)(nil)

func makeBserverLeveldbStorage(codec Codec, db *leveldb.DB) *bserverLeveldbStorage {
	return &bserverLeveldbStorage{codec: codec, db: db}
}

func (s *bserverLeveldbStorage) getLocked(id BlockID) (blockEntry, error) {
	buf, err := s.db.Get(id.Bytes(), nil)
	if err != nil {
		return blockEntry{}, err
	}

	var entry blockEntry
	err = s.codec.Decode(buf, &entry)
	if err != nil {
		return blockEntry{}, err
	}

	return entry, nil
}

func (s *bserverLeveldbStorage) get(id BlockID) (blockEntry, error) {
	s.lock.RLock()
	defer s.lock.RUnlock()
	entry, err := s.getLocked(id)
	if err != nil {
		if err == leveldb.ErrNotFound {
			err = BServerErrorBlockNonExistent{}
		}
		return blockEntry{}, err
	}
	return entry, nil
}

func (s *bserverLeveldbStorage) getAll(tlf TlfID) (
	map[BlockID]map[BlockRefNonce]bool, error) {
	return nil,
		errors.New("getAll not yet implemented for bserverLeveldbStorage")
}

func (s *bserverLeveldbStorage) putLocked(id BlockID, entry blockEntry) error {
	entryBuf, err := s.codec.Encode(entry)
	if err != nil {
		return err
	}

	return s.db.Put(id.Bytes(), entryBuf, nil)
}

func (s *bserverLeveldbStorage) put(id BlockID, entry blockEntry) error {
	s.lock.Lock()
	defer s.lock.Unlock()
	return s.putLocked(id, entry)
}

func (s *bserverLeveldbStorage) addReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	entry, err := s.getLocked(id)
	if err != nil {
		if err == leveldb.ErrNotFound {
			return IncrementMissingBlockError{id}
		}
		return err
	}

	for _, status := range entry.Refs {
		if status == blockRef {
			entry.Refs[refNonce] = blockRef
			return s.putLocked(id, entry)
		}
	}
	return BServerErrorBlockArchived{""}
}

func (s *bserverLeveldbStorage) removeReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	entry, err := s.getLocked(id)
	if err != nil {
		if err == leveldb.ErrNotFound {
			// This block is already gone; no error.
			return nil
		}
		return err
	}

	delete(entry.Refs, refNonce)
	if len(entry.Refs) == 0 {
		return s.db.Delete(id.Bytes(), nil)
	}
	return s.putLocked(id, entry)
}

func (s *bserverLeveldbStorage) archiveReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	entry, err := s.getLocked(id)
	if err != nil {
		if err == leveldb.ErrNotFound {
			return ArchiveMissingBlockError{id, refNonce}
		}
		return err
	}

	entry.Refs[refNonce] = archivedBlockRef
	return s.putLocked(id, entry)
}

func (s *bserverLeveldbStorage) shutdown() {
	if s.db != nil {
		s.db.Close()
	}
}
<commit_msg>bserver_local_storage: don't archive references that don't exist<commit_after>package libkbfs

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/syndtr/goleveldb/leveldb"
)

type blockRefLocalStatus int

const (
	noBlockRef blockRefLocalStatus = iota
	blockRef
	archivedBlockRef
)

type blockEntry struct {
	// These fields are only exported for serialization purposes.
	BlockData     []byte
	Refs          map[BlockRefNonce]blockRefLocalStatus
	KeyServerHalf BlockCryptKeyServerHalf
	Tlf           TlfID
}

// bserverLocalStorage abstracts the various methods of storing blocks
// for bserverLocal.
type bserverLocalStorage interface {
	get(id BlockID) (blockEntry, error)
	getAll(tlf TlfID) (map[BlockID]map[BlockRefNonce]bool, error)
	put(id BlockID, entry blockEntry) error
	addReference(id BlockID, refNonce BlockRefNonce) error
	removeReference(id BlockID, refNonce BlockRefNonce) error
	archiveReference(id BlockID, refNonce BlockRefNonce) error
	shutdown()
}

// bserverMemStorage stores block data in an in-memory map.
type bserverMemStorage struct {
	lock sync.RWMutex
	m    map[BlockID]blockEntry
}

var _ bserverLocalStorage = (*bserverMemStorage)(nil)

func makeBserverMemStorage() *bserverMemStorage {
	return &bserverMemStorage{m: make(map[BlockID]blockEntry)}
}

func (s *bserverMemStorage) get(id BlockID) (blockEntry, error) {
	s.lock.RLock()
	defer s.lock.RUnlock()

	entry, ok := s.m[id]
	if !ok {
		return blockEntry{}, BServerErrorBlockNonExistent{}
	}
	return entry, nil
}

func (s *bserverMemStorage) getAll(tlf TlfID) (
	map[BlockID]map[BlockRefNonce]bool, error) {
	res := make(map[BlockID]map[BlockRefNonce]bool)
	s.lock.RLock()
	defer s.lock.RUnlock()

	for id, entry := range s.m {
		if entry.Tlf != tlf {
			continue
		}
		res[id] = make(map[BlockRefNonce]bool)
		for ref := range entry.Refs {
			res[id][ref] = true
		}
	}
	return res, nil
}

func (s *bserverMemStorage) put(id BlockID, entry blockEntry) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	s.m[id] = entry
	return nil
}

func (s *bserverMemStorage) addReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	entry, ok := s.m[id]
	if !ok {
		return IncrementMissingBlockError{id}
	}

	// only add it if there's a non-archived reference
	for _, status := range entry.Refs {
		if status == blockRef {
			entry.Refs[refNonce] = blockRef
			s.m[id] = entry
			return nil
		}
	}
	return BServerErrorBlockArchived{""}
}

func (s *bserverMemStorage) removeReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	entry, ok := s.m[id]
	if !ok {
		// This block is already gone; no error.
		return nil
	}

	delete(entry.Refs, refNonce)
	if len(entry.Refs) == 0 {
		delete(s.m, id)
	} else {
		s.m[id] = entry
	}
	return nil
}

func (s *bserverMemStorage) archiveReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	entry, ok := s.m[id]
	if !ok {
		return ArchiveMissingBlockError{id, refNonce}
	}

	_, ok = entry.Refs[refNonce]
	if !ok {
		return ArchiveMissingBlockError{id, refNonce}
	}

	entry.Refs[refNonce] = archivedBlockRef
	s.m[id] = entry
	return nil
}

func (s *bserverMemStorage) shutdown() {
	// Nothing to do.
}

// bserverFileStorage stores block data in flat files on disk.
type bserverFileStorage struct {
	codec Codec
	lock  sync.RWMutex
	dir   string
}

var _ bserverLocalStorage = (*bserverFileStorage)(nil)

func makeBserverFileStorage(codec Codec, dir string) *bserverFileStorage {
	return &bserverFileStorage{codec: codec, dir: dir}
}

// Store each block in its own file with name equal to the hex-encoded
// blockID. Splay the filenames over 256^2 subdirectories (one byte
// for the hash type plus the first byte of the hash data) using the
// first four characters of the name to keep the number of directories
// in dir itself to a manageable number, similar to git.
func (s *bserverFileStorage) buildPath(id BlockID) string {
	idStr := id.String()
	return filepath.Join(s.dir, idStr[:4], idStr[4:])
}

func (s *bserverFileStorage) getLocked(p string) (blockEntry, error) {
	buf, err := ioutil.ReadFile(p)
	if err != nil {
		return blockEntry{}, err
	}

	var entry blockEntry
	err = s.codec.Decode(buf, &entry)
	if err != nil {
		return blockEntry{}, err
	}

	return entry, nil
}

func (s *bserverFileStorage) get(id BlockID) (blockEntry, error) {
	s.lock.RLock()
	defer s.lock.RUnlock()
	entry, err := s.getLocked(s.buildPath(id))
	if err != nil {
		if os.IsNotExist(err) {
			err = BServerErrorBlockNonExistent{}
		}
		return blockEntry{}, err
	}
	return entry, nil
}

func (s *bserverFileStorage) getAll(tlf TlfID) (
	map[BlockID]map[BlockRefNonce]bool, error) {
	return nil, errors.New("getAll not yet implemented for bserverFileStorage")
}

func (s *bserverFileStorage) putLocked(p string, entry blockEntry) error {
	entryBuf, err := s.codec.Encode(entry)
	if err != nil {
		return err
	}

	err = os.MkdirAll(filepath.Dir(p), 0700)
	if err != nil {
		return err
	}

	return ioutil.WriteFile(p, entryBuf, 0600)
}

func (s *bserverFileStorage) put(id BlockID, entry blockEntry) error {
	s.lock.Lock()
	defer s.lock.Unlock()
	return s.putLocked(s.buildPath(id), entry)
}

func (s *bserverFileStorage) addReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	p := s.buildPath(id)
	entry, err := s.getLocked(p)
	if err != nil {
		if os.IsNotExist(err) {
			return IncrementMissingBlockError{id}
		}
		return err
	}

	// only add it if there's a non-archived reference
	for _, status := range entry.Refs {
		if status == blockRef {
			entry.Refs[refNonce] = blockRef
			return s.putLocked(p, entry)
		}
	}
	return BServerErrorBlockArchived{""}
}

func (s *bserverFileStorage) removeReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	p := s.buildPath(id)
	entry, err := s.getLocked(p)
	if err != nil {
		if os.IsNotExist(err) {
			// This block is already gone; no error.
			return nil
		}
		return err
	}

	delete(entry.Refs, refNonce)
	if len(entry.Refs) == 0 {
		return os.Remove(p)
	}
	return s.putLocked(p, entry)
}

func (s *bserverFileStorage) archiveReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	p := s.buildPath(id)
	entry, err := s.getLocked(p)
	if err != nil {
		if os.IsNotExist(err) {
			return ArchiveMissingBlockError{id, refNonce}
		}
		return err
	}

	_, ok := entry.Refs[refNonce]
	if !ok {
		return ArchiveMissingBlockError{id, refNonce}
	}

	entry.Refs[refNonce] = archivedBlockRef
	return s.putLocked(p, entry)
}

func (s *bserverFileStorage) shutdown() {
	// Nothing to do.
}

// bserverLeveldbStorage stores block data in a LevelDB database. This
// is kept around only for benchmarking purposes.
type bserverLeveldbStorage struct {
	codec Codec
	lock  sync.RWMutex
	db    *leveldb.DB
}

var _ bserverLocalStorage = (*bserverLeveldbStorage)(nil)

func makeBserverLeveldbStorage(codec Codec, db *leveldb.DB) *bserverLeveldbStorage {
	return &bserverLeveldbStorage{codec: codec, db: db}
}

func (s *bserverLeveldbStorage) getLocked(id BlockID) (blockEntry, error) {
	buf, err := s.db.Get(id.Bytes(), nil)
	if err != nil {
		return blockEntry{}, err
	}

	var entry blockEntry
	err = s.codec.Decode(buf, &entry)
	if err != nil {
		return blockEntry{}, err
	}

	return entry, nil
}

func (s *bserverLeveldbStorage) get(id BlockID) (blockEntry, error) {
	s.lock.RLock()
	defer s.lock.RUnlock()
	entry, err := s.getLocked(id)
	if err != nil {
		if err == leveldb.ErrNotFound {
			err = BServerErrorBlockNonExistent{}
		}
		return blockEntry{}, err
	}
	return entry, nil
}

func (s *bserverLeveldbStorage) getAll(tlf TlfID) (
	map[BlockID]map[BlockRefNonce]bool, error) {
	return nil,
		errors.New("getAll not yet implemented for bserverLeveldbStorage")
}

func (s *bserverLeveldbStorage) putLocked(id BlockID, entry blockEntry) error {
	entryBuf, err := s.codec.Encode(entry)
	if err != nil {
		return err
	}

	return s.db.Put(id.Bytes(), entryBuf, nil)
}

func (s *bserverLeveldbStorage) put(id BlockID, entry blockEntry) error {
	s.lock.Lock()
	defer s.lock.Unlock()
	return s.putLocked(id, entry)
}

func (s *bserverLeveldbStorage) addReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	entry, err := s.getLocked(id)
	if err != nil {
		if err == leveldb.ErrNotFound {
			return IncrementMissingBlockError{id}
		}
		return err
	}

	for _, status := range entry.Refs {
		if status == blockRef {
			entry.Refs[refNonce] = blockRef
			return s.putLocked(id, entry)
		}
	}
	return BServerErrorBlockArchived{""}
}

func (s *bserverLeveldbStorage) removeReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	entry, err := s.getLocked(id)
	if err != nil {
		if err == leveldb.ErrNotFound {
			// This block is already gone; no error.
			return nil
		}
		return err
	}

	delete(entry.Refs, refNonce)
	if len(entry.Refs) == 0 {
		return s.db.Delete(id.Bytes(), nil)
	}
	return s.putLocked(id, entry)
}

func (s *bserverLeveldbStorage) archiveReference(id BlockID, refNonce BlockRefNonce) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	entry, err := s.getLocked(id)
	if err != nil {
		if err == leveldb.ErrNotFound {
			return ArchiveMissingBlockError{id, refNonce}
		}
		return err
	}

	_, ok := entry.Refs[refNonce]
	if !ok {
		return ArchiveMissingBlockError{id, refNonce}
	}

	entry.Refs[refNonce] = archivedBlockRef
	return s.putLocked(id, entry)
}

func (s *bserverLeveldbStorage) shutdown() {
	if s.db != nil {
		s.db.Close()
	}
}
<|endoftext|>


<commit_before>package mbtiles

import (
	"database/sql"
	"github.com/hokkaido/blink"
	"log"
	"net/url"
	"os"
	_ "third_party/github.com/mattn/go-sqlite3"
)

func init() {
	blink.RegisterProvider("mbtiles", NewProvider, NewConfig)
}

type MBTile struct {
	batchSize int
	db        *sql.DB
	open      bool
}

type Config struct {
	PathToDb  string
	BatchSize int
}

func NewConfig() interface{} {
	return &Config{BatchSize: 100}
}

func NewProvider(providerConfig interface{}) (blink.Provider, error) {

	config, ok := providerConfig.(*Config)
	if !ok {
		panic("wrong config")
	}
	log.Print("OMG")
	log.Print(config.PathToDb)
	_, err := os.Stat(config.PathToDb)
	if err != nil {
		return nil, err
	}

	db, err := sql.Open("sqlite3", config.PathToDb)

	if err != nil {
		return nil, err
	}

	return &MBTile{db: db, batchSize: config.BatchSize, open: true}, nil
}

func (mbtile *MBTile) GetTile(zoom int, x int, y int) ([]byte, error) {

	var sql = "SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?"

	// Flip y-Axis
	y = (1 << uint(zoom)) - 1 - y

	stmt, err := mbtile.db.Prepare(sql)
	if err != nil {
		return nil, err
	}

	defer stmt.Close()

	var tileData []byte

	err = stmt.QueryRow(zoom, x, y).Scan(&tileData)
	if err != nil {
		return nil, err
	}

	return tileData, nil
}

// Select a grid and its data from an mbtiles database
func (mbtile *MBTile) GetGrid(zoom int, x int, y int) ([]byte, error) {

	var sqlgrid = "SELECT grid FROM grids WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?"
	//var sqljson = "SELECT key_name, key_json FROM grid_data WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?"

	stmt, err := mbtile.db.Prepare(sqlgrid)
	if err != nil {
		return nil, err
	}

	defer stmt.Close()

	var gridData []byte

	err = stmt.QueryRow(zoom, x, y).Scan(&gridData)
	if err != nil {
		return nil, err
	}

	return gridData, nil
}

func (mbtile *MBTile) GetMetadata(u *url.URL) {

	rows, err := mbtile.db.Query("Select name, value FROM metadata")
	if err != nil {
		return
	}

	//values := make(map[string]string)

	defer rows.Close()

	for rows.Next() {
		var name, value string
		rows.Scan(&name, &value)
		switch name {
		case "json":

		case "":

		case "tatat":
		}

	}

}

// Closes the underlying Sqlite Database
func (mbtile *MBTile) Close() {
	mbtile.db.Close()
}

type Metadata struct {
	scheme   string
	basename string
	id       string
	minZoom  int
	maxZoom  int
	Values   map[string]string
}

func integrityCheck() {

}
<commit_msg>Fix third_party import path<commit_after>package mbtiles

import (
	"database/sql"
	"github.com/hokkaido/blink"
	"log"
	"net/url"
	"os"
	_ "github.com/hokkaido/blink-mbtiles/third_party/github.com/mattn/go-sqlite3"
)

func init() {
	blink.RegisterProvider("mbtiles", NewProvider, NewConfig)
}

type MBTile struct {
	batchSize int
	db        *sql.DB
	open      bool
}

type Config struct {
	PathToDb  string
	BatchSize int
}

func NewConfig() interface{} {
	return &Config{BatchSize: 100}
}

func NewProvider(providerConfig interface{}) (blink.Provider, error) {

	config, ok := providerConfig.(*Config)
	if !ok {
		panic("wrong config")
	}
	log.Print("OMG")
	log.Print(config.PathToDb)
	_, err := os.Stat(config.PathToDb)
	if err != nil {
		return nil, err
	}

	db, err := sql.Open("sqlite3", config.PathToDb)

	if err != nil {
		return nil, err
	}

	return &MBTile{db: db, batchSize: config.BatchSize, open: true}, nil
}

func (mbtile *MBTile) GetTile(zoom int, x int, y int) ([]byte, error) {

	var sql = "SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?"

	// Flip y-Axis
	y = (1 << uint(zoom)) - 1 - y

	stmt, err := mbtile.db.Prepare(sql)
	if err != nil {
		return nil, err
	}

	defer stmt.Close()

	var tileData []byte

	err = stmt.QueryRow(zoom, x, y).Scan(&tileData)
	if err != nil {
		return nil, err
	}

	return tileData, nil
}

// Select a grid and its data from an mbtiles database
func (mbtile *MBTile) GetGrid(zoom int, x int, y int) ([]byte, error) {

	var sqlgrid = "SELECT grid FROM grids WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?"
	//var sqljson = "SELECT key_name, key_json FROM grid_data WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?"

	stmt, err := mbtile.db.Prepare(sqlgrid)
	if err != nil {
		return nil, err
	}

	defer stmt.Close()

	var gridData []byte

	err = stmt.QueryRow(zoom, x, y).Scan(&gridData)
	if err != nil {
		return nil, err
	}

	return gridData, nil
}

func (mbtile *MBTile) GetMetadata(u *url.URL) {

	rows, err := mbtile.db.Query("Select name, value FROM metadata")
	if err != nil {
		return
	}

	//values := make(map[string]string)

	defer rows.Close()

	for rows.Next() {
		var name, value string
		rows.Scan(&name, &value)
		switch name {
		case "json":

		case "":

		case "tatat":
		}

	}

}

// Closes the underlying Sqlite Database
func (mbtile *MBTile) Close() {
	mbtile.db.Close()
}

type Metadata struct {
	scheme   string
	basename string
	id       string
	minZoom  int
	maxZoom  int
	Values   map[string]string
}

func integrityCheck() {

}
<|endoftext|>


<commit_before>package sqlstore

import (
	"time"

	"github.com/raintank/raintank-apps/task-server/model"
)

func AddAgentSession(a *model.AgentSession) error {
	sess, err := newSession(true, "agent_session")
	if err != nil {
		return err
	}
	defer sess.Cleanup()

	if err := addAgentSession(sess, a); err != nil {
		return err
	}
	sess.Complete()
	return err
}

func addAgentSession(sess *session, a *model.AgentSession) error {
	if _, err := sess.Insert(a); err != nil {
		return err
	}
	// set Agent state to online.
	rawSql := "UPDATE agent set online=1, online_change=? where id=?"
	_, err := sess.Exec(rawSql, time.Now(), a.AgentId)
	if err != nil {
		return err
	}
	return nil
}

func DeleteAgentSession(a *model.AgentSession) error {
	sess, err := newSession(true, "agent_session")
	if err != nil {
		return err
	}
	defer sess.Cleanup()
	if err = deleteAgentSession(sess, a); err != nil {
		return err
	}
	sess.Complete()
	return nil
}

func deleteAgentSession(sess *session, a *model.AgentSession) error {
	var rawSql = "DELETE FROM agent_session WHERE id=?"
	_, err := sess.Exec(rawSql, a.Id)
	if err != nil {
		return err
	}

	// we query here to prevent race conditions when agents dicsonnect from one task-server node
	// and connect to another.  The new connection may establish before the old connection times out.
	total, err := sess.Where("agent_session.agent_id=?", a.AgentId).Count(&model.AgentSession{})
	if err != nil {
		return err
	}
	if total == 0 {
		rawSql := "UPDATE agent set online=0, online_change=? where id=?"
		_, err := sess.Exec(rawSql, a.AgentId, time.Now())
		if err != nil {
			return err
		}
	}
	return nil
}

func DeleteAgentSessionsByServer(server string) error {
	sess, err := newSession(true, "agent_session")
	if err != nil {
		return err
	}
	defer sess.Cleanup()
	if err = deleteAgentSessionsByServer(sess, server); err != nil {
		return err
	}
	sess.Complete()
	return nil
}

func deleteAgentSessionsByServer(sess *session, server string) error {
	var rawSql = "DELETE FROM agent_session WHERE server=?"
	_, err := sess.Exec(rawSql, server)
	if err != nil {
		return err
	}

	// set the online state for all agents that now have no sessions.
	rawSql = `UPDATE agent set online=0, online_change=? where id IN (SELECT t.id
	FROM (SELECT id from agent WHERE agent.online=1) as t LEFT JOIN agent_session ON t.id = agent_session.agent_id
	WHERE agent_session.id is NULL)`
	_, err = sess.Exec(rawSql, time.Now())
	if err != nil {
		return err
	}
	return nil
}

func GetAgentSessionsByServer(server string) ([]*model.AgentSession, error) {
	sess, err := newSession(false, "agent_session")
	if err != nil {
		return nil, err
	}
	return getAgentSessionsByServer(sess, server)
}

func getAgentSessionsByServer(sess *session, server string) ([]*model.AgentSession, error) {
	agentSessions := make([]*model.AgentSession, 0)
	err := sess.Where("server=?", server).Find(&agentSessions)
	if err != nil {
		return nil, err
	}
	return agentSessions, nil
}

func GetAgentSessionsByAgentId(agentId int64) ([]*model.AgentSession, error) {
	sess, err := newSession(false, "agent_session")
	if err != nil {
		return nil, err
	}
	return getAgentSessionsByAgentId(sess, agentId)
}

func getAgentSessionsByAgentId(sess *session, agentId int64) ([]*model.AgentSession, error) {
	agentSessions := make([]*model.AgentSession, 0)
	err := sess.Where("agent_id=?", agentId).Find(&agentSessions)
	if err != nil {
		return nil, err
	}
	return agentSessions, nil
}
<commit_msg>log when agent being marked as offline.<commit_after>package sqlstore

import (
	"time"

	"github.com/grafana/grafana/pkg/log"
	"github.com/raintank/raintank-apps/task-server/model"
)

func AddAgentSession(a *model.AgentSession) error {
	sess, err := newSession(true, "agent_session")
	if err != nil {
		return err
	}
	defer sess.Cleanup()

	if err := addAgentSession(sess, a); err != nil {
		return err
	}
	sess.Complete()
	return err
}

func addAgentSession(sess *session, a *model.AgentSession) error {
	if _, err := sess.Insert(a); err != nil {
		return err
	}
	// set Agent state to online.
	rawSql := "UPDATE agent set online=1, online_change=? where id=?"
	_, err := sess.Exec(rawSql, time.Now(), a.AgentId)
	if err != nil {
		return err
	}
	return nil
}

func DeleteAgentSession(a *model.AgentSession) error {
	sess, err := newSession(true, "agent_session")
	if err != nil {
		return err
	}
	defer sess.Cleanup()
	if err = deleteAgentSession(sess, a); err != nil {
		return err
	}
	sess.Complete()
	return nil
}

func deleteAgentSession(sess *session, a *model.AgentSession) error {
	var rawSql = "DELETE FROM agent_session WHERE id=?"
	_, err := sess.Exec(rawSql, a.Id)
	if err != nil {
		return err
	}

	// we query here to prevent race conditions when agents dicsonnect from one task-server node
	// and connect to another.  The new connection may establish before the old connection times out.
	total, err := sess.Where("agent_session.agent_id=?", a.AgentId).Count(&model.AgentSession{})
	if err != nil {
		return err
	}
	if total == 0 {
		log.Info("AgentId %d has no sessions. Marking as offline.", a.AgentId)
		rawSql := "UPDATE agent set online=0, online_change=? where id=?"
		_, err := sess.Exec(rawSql, a.AgentId, time.Now())
		if err != nil {
			return err
		}
	}
	return nil
}

func DeleteAgentSessionsByServer(server string) error {
	sess, err := newSession(true, "agent_session")
	if err != nil {
		return err
	}
	defer sess.Cleanup()
	if err = deleteAgentSessionsByServer(sess, server); err != nil {
		return err
	}
	sess.Complete()
	return nil
}

func deleteAgentSessionsByServer(sess *session, server string) error {
	var rawSql = "DELETE FROM agent_session WHERE server=?"
	_, err := sess.Exec(rawSql, server)
	if err != nil {
		return err
	}

	// set the online state for all agents that now have no sessions.
	rawSql = `UPDATE agent set online=0, online_change=? where id IN (SELECT t.id
	FROM (SELECT id from agent WHERE agent.online=1) as t LEFT JOIN agent_session ON t.id = agent_session.agent_id
	WHERE agent_session.id is NULL)`
	_, err = sess.Exec(rawSql, time.Now())
	if err != nil {
		return err
	}
	return nil
}

func GetAgentSessionsByServer(server string) ([]*model.AgentSession, error) {
	sess, err := newSession(false, "agent_session")
	if err != nil {
		return nil, err
	}
	return getAgentSessionsByServer(sess, server)
}

func getAgentSessionsByServer(sess *session, server string) ([]*model.AgentSession, error) {
	agentSessions := make([]*model.AgentSession, 0)
	err := sess.Where("server=?", server).Find(&agentSessions)
	if err != nil {
		return nil, err
	}
	return agentSessions, nil
}

func GetAgentSessionsByAgentId(agentId int64) ([]*model.AgentSession, error) {
	sess, err := newSession(false, "agent_session")
	if err != nil {
		return nil, err
	}
	return getAgentSessionsByAgentId(sess, agentId)
}

func getAgentSessionsByAgentId(sess *session, agentId int64) ([]*model.AgentSession, error) {
	agentSessions := make([]*model.AgentSession, 0)
	err := sess.Where("agent_id=?", agentId).Find(&agentSessions)
	if err != nil {
		return nil, err
	}
	return agentSessions, nil
}
<|endoftext|>


<commit_before>package mysql

import (
	"encoding/binary"
	"fmt"
	"math"
	"strconv"

	log "github.com/ngaut/logging"
	"github.com/wandoulabs/cm/hack"
)

type RowData []byte
type Value interface{}
type RowValue []Value

func Raw(t byte, val Value, isUnsigned bool) []byte {
	if val == nil {
		return nil
	}

	var ret []byte
	switch t {
	case MYSQL_TYPE_TINY, MYSQL_TYPE_SHORT, MYSQL_TYPE_INT24, MYSQL_TYPE_LONG,
		MYSQL_TYPE_LONGLONG, MYSQL_TYPE_YEAR:
		if isUnsigned {
			ret = []byte(strconv.FormatUint(val.(uint64), 10))
		} else {
			ret = []byte(strconv.FormatInt(val.(int64), 10))
		}

	case MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE:
		ret = []byte(strconv.FormatFloat(val.(float64), 'f', 16, 64))
	case MYSQL_TYPE_VARCHAR:
		ret = hack.Slice(val.(string))
	default:
		var ok bool
		ret, ok = val.([]byte)
		if !ok {
			log.Errorf("%v, %+v, %T", t, val, val)
		}
	}

	return ret
}

func (p RowData) Parse(f []*Field, binary bool) (RowValue, error) {
	if binary {
		return p.ParseBinary(f)
	}

	return p.ParseText(f)
}

func (p RowData) ParseText(f []*Field) (RowValue, error) {
	data := make([]Value, len(f))
	var err error
	var v []byte
	var isNull, isUnsigned bool
	var pos int = 0
	var n int = 0

	for i := range f {
		v, isNull, n, err = LengthEnodedString(p[pos:])
		if err != nil {
			return nil, err
		}

		pos += n

		if isNull {
			data[i] = nil
		} else {
			isUnsigned = (f[i].Flag&UNSIGNED_FLAG > 0)

			switch f[i].Type {
			case MYSQL_TYPE_TINY, MYSQL_TYPE_SHORT, MYSQL_TYPE_INT24, MYSQL_TYPE_LONG,
				MYSQL_TYPE_LONGLONG, MYSQL_TYPE_YEAR:
				if isUnsigned {
					data[i], err = strconv.ParseUint(string(v), 10, 64)
				} else {
					data[i], err = strconv.ParseInt(string(v), 10, 64)
				}
			case MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE:
				data[i], err = strconv.ParseFloat(string(v), 64)
			default:
				data[i] = v
			}

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

	return data, nil
}

func (p RowData) ParseBinary(f []*Field) ([]Value, error) {
	data := make([]Value, len(f))

	if p[0] != OK_HEADER {
		return nil, ErrMalformPacket
	}

	pos := 1 + ((len(f) + 7 + 2) >> 3)

	nullBitmap := p[1:pos]

	var isUnsigned bool
	var isNull bool
	var n int
	var err error
	var v []byte
	for i := range data {
		if nullBitmap[(i+2)/8]&(1<<(uint(i+2)%8)) > 0 {
			data[i] = nil
			continue
		}

		isUnsigned = f[i].Flag&UNSIGNED_FLAG > 0

		switch f[i].Type {
		case MYSQL_TYPE_NULL:
			data[i] = nil
			continue

		case MYSQL_TYPE_TINY:
			if isUnsigned {
				data[i] = uint64(p[pos])
			} else {
				data[i] = int64(p[pos])
			}
			pos++
			continue

		case MYSQL_TYPE_SHORT, MYSQL_TYPE_YEAR:
			if isUnsigned {
				data[i] = uint64(binary.LittleEndian.Uint16(p[pos : pos+2]))
			} else {
				data[i] = int64((binary.LittleEndian.Uint16(p[pos : pos+2])))
			}
			pos += 2
			continue

		case MYSQL_TYPE_INT24, MYSQL_TYPE_LONG:
			if isUnsigned {
				data[i] = uint64(binary.LittleEndian.Uint32(p[pos : pos+4]))
			} else {
				data[i] = int64(binary.LittleEndian.Uint32(p[pos : pos+4]))
			}
			pos += 4
			continue

		case MYSQL_TYPE_LONGLONG:
			if isUnsigned {
				data[i] = binary.LittleEndian.Uint64(p[pos : pos+8])
			} else {
				data[i] = int64(binary.LittleEndian.Uint64(p[pos : pos+8]))
			}
			pos += 8
			continue

		case MYSQL_TYPE_FLOAT:
			data[i] = float64(math.Float32frombits(binary.LittleEndian.Uint32(p[pos : pos+4])))
			pos += 4
			continue

		case MYSQL_TYPE_DOUBLE:
			data[i] = math.Float64frombits(binary.LittleEndian.Uint64(p[pos : pos+8]))
			pos += 8
			continue

		case MYSQL_TYPE_DECIMAL, MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR,
			MYSQL_TYPE_BIT, MYSQL_TYPE_ENUM, MYSQL_TYPE_SET, MYSQL_TYPE_TINY_BLOB,
			MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_BLOB,
			MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY:
			v, isNull, n, err = LengthEnodedString(p[pos:])
			pos += n
			if err != nil {
				return nil, err
			}

			if !isNull {
				data[i] = v
				continue
			} else {
				data[i] = nil
				continue
			}
		case MYSQL_TYPE_DATE, MYSQL_TYPE_NEWDATE:
			var num uint64
			num, isNull, n = LengthEncodedInt(p[pos:])

			pos += n

			if isNull {
				data[i] = nil
				continue
			}

			data[i], err = FormatBinaryDate(int(num), p[pos:])
			pos += int(num)

			if err != nil {
				return nil, err
			}

		case MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_DATETIME:
			var num uint64
			num, isNull, n = LengthEncodedInt(p[pos:])

			pos += n

			if isNull {
				data[i] = nil
				continue
			}

			data[i], err = FormatBinaryDateTime(int(num), p[pos:])
			pos += int(num)

			if err != nil {
				return nil, err
			}

		case MYSQL_TYPE_TIME:
			var num uint64
			num, isNull, n = LengthEncodedInt(p[pos:])

			pos += n

			if isNull {
				data[i] = nil
				continue
			}

			data[i], err = FormatBinaryTime(int(num), p[pos:])
			pos += int(num)

			if err != nil {
				return nil, err
			}

		default:
			return nil, fmt.Errorf("Stmt Unknown FieldType %d %s", f[i].Type, f[i].Name)
		}
	}

	return data, nil
}

type Resultset struct {
	Fields     []*Field
	FieldNames map[string]int
	Values     []RowValue

	RowDatas []RowData
}

func (r *Resultset) RowNumber() int {
	return len(r.Values)
}

func (r *Resultset) ColumnNumber() int {
	return len(r.Fields)
}

func (r *Resultset) GetValue(row, column int) (interface{}, error) {
	if row >= len(r.Values) || row < 0 {
		return nil, fmt.Errorf("invalid row index %d", row)
	}

	if column >= len(r.Fields) || column < 0 {
		return nil, fmt.Errorf("invalid column index %d", column)
	}

	return r.Values[row][column], nil
}

func (r *Resultset) NameIndex(name string) (int, error) {
	if column, ok := r.FieldNames[name]; ok {
		return column, nil
	} else {
		return 0, fmt.Errorf("invalid field name %s", name)
	}
}

func (r *Resultset) GetValueByName(row int, name string) (interface{}, error) {
	if column, err := r.NameIndex(name); err != nil {
		return nil, err
	} else {
		return r.GetValue(row, column)
	}
}

func (r *Resultset) IsNull(row, column int) (bool, error) {
	d, err := r.GetValue(row, column)
	if err != nil {
		return false, err
	}

	return d == nil, nil
}

func (r *Resultset) IsNullByName(row int, name string) (bool, error) {
	if column, err := r.NameIndex(name); err != nil {
		return false, err
	} else {
		return r.IsNull(row, column)
	}
}

func (r *Resultset) GetUint(row, column int) (uint64, error) {
	d, err := r.GetValue(row, column)
	if err != nil {
		return 0, err
	}

	switch v := d.(type) {
	case uint64:
		return v, nil
	case int64:
		return uint64(v), nil
	case float64:
		return uint64(v), nil
	case string:
		return strconv.ParseUint(v, 10, 64)
	case []byte:
		return strconv.ParseUint(string(v), 10, 64)
	case nil:
		return 0, nil
	default:
		return 0, fmt.Errorf("data type is %T", v)
	}
}

func (r *Resultset) GetUintByName(row int, name string) (uint64, error) {
	if column, err := r.NameIndex(name); err != nil {
		return 0, err
	} else {
		return r.GetUint(row, column)
	}
}

func (r *Resultset) GetInt(row, column int) (int64, error) {
	v, err := r.GetUint(row, column)
	if err != nil {
		return 0, err
	}

	return int64(v), nil
}

func (r *Resultset) GetIntByName(row int, name string) (int64, error) {
	v, err := r.GetUintByName(row, name)
	if err != nil {
		return 0, err
	}

	return int64(v), nil
}

func (r *Resultset) GetFloat(row, column int) (float64, error) {
	d, err := r.GetValue(row, column)
	if err != nil {
		return 0, err
	}

	switch v := d.(type) {
	case float64:
		return v, nil
	case uint64:
		return float64(v), nil
	case int64:
		return float64(v), nil
	case string:
		return strconv.ParseFloat(v, 64)
	case []byte:
		return strconv.ParseFloat(string(v), 64)
	case nil:
		return 0, nil
	default:
		return 0, fmt.Errorf("data type is %T", v)
	}
}

func (r *Resultset) GetFloatByName(row int, name string) (float64, error) {
	if column, err := r.NameIndex(name); err != nil {
		return 0, err
	} else {
		return r.GetFloat(row, column)
	}
}

func (r *Resultset) GetString(row, column int) (string, error) {
	d, err := r.GetValue(row, column)
	if err != nil {
		return "", err
	}

	switch v := d.(type) {
	case string:
		return v, nil
	case []byte:
		return hack.String(v), nil
	case int64:
		return strconv.FormatInt(v, 10), nil
	case uint64:
		return strconv.FormatUint(v, 10), nil
	case float64:
		return strconv.FormatFloat(v, 'f', -1, 64), nil
	case nil:
		return "", nil
	default:
		return "", fmt.Errorf("data type is %T", v)
	}
}

func (r *Resultset) GetStringByName(row int, name string) (string, error) {
	if column, err := r.NameIndex(name); err != nil {
		return "", err
	} else {
		return r.GetString(row, column)
	}
}
<commit_msg>fix MYSQL_TYPE_VARCHAR can be string or []byte<commit_after>package mysql

import (
	"encoding/binary"
	"fmt"
	"math"
	"strconv"

	log "github.com/ngaut/logging"
	"github.com/wandoulabs/cm/hack"
)

type RowData []byte
type Value interface{}
type RowValue []Value

func Raw(t byte, val Value, isUnsigned bool) []byte {
	if val == nil {
		return nil
	}

	var ret []byte
	switch t {
	case MYSQL_TYPE_TINY, MYSQL_TYPE_SHORT, MYSQL_TYPE_INT24, MYSQL_TYPE_LONG,
		MYSQL_TYPE_LONGLONG, MYSQL_TYPE_YEAR:
		if isUnsigned {
			ret = []byte(strconv.FormatUint(val.(uint64), 10))
		} else {
			ret = []byte(strconv.FormatInt(val.(int64), 10))
		}

	case MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE:
		ret = []byte(strconv.FormatFloat(val.(float64), 'f', 16, 64))
	case MYSQL_TYPE_VARCHAR:
		str, ok := val.(string)
		if ok {
			ret = hack.Slice(str)
			break
		}

		fallthrough
	default:
		var ok bool
		ret, ok = val.([]byte)
		if !ok {
			log.Errorf("%v, %+v, %T", t, val, val)
		}
	}

	return ret
}

func (p RowData) Parse(f []*Field, binary bool) (RowValue, error) {
	if binary {
		return p.ParseBinary(f)
	}

	return p.ParseText(f)
}

func (p RowData) ParseText(f []*Field) (RowValue, error) {
	data := make([]Value, len(f))
	var err error
	var v []byte
	var isNull, isUnsigned bool
	var pos int = 0
	var n int = 0

	for i := range f {
		v, isNull, n, err = LengthEnodedString(p[pos:])
		if err != nil {
			return nil, err
		}

		pos += n

		if isNull {
			data[i] = nil
		} else {
			isUnsigned = (f[i].Flag&UNSIGNED_FLAG > 0)

			switch f[i].Type {
			case MYSQL_TYPE_TINY, MYSQL_TYPE_SHORT, MYSQL_TYPE_INT24, MYSQL_TYPE_LONG,
				MYSQL_TYPE_LONGLONG, MYSQL_TYPE_YEAR:
				if isUnsigned {
					data[i], err = strconv.ParseUint(string(v), 10, 64)
				} else {
					data[i], err = strconv.ParseInt(string(v), 10, 64)
				}
			case MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE:
				data[i], err = strconv.ParseFloat(string(v), 64)
			default:
				data[i] = v
			}

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

	return data, nil
}

func (p RowData) ParseBinary(f []*Field) ([]Value, error) {
	data := make([]Value, len(f))

	if p[0] != OK_HEADER {
		return nil, ErrMalformPacket
	}

	pos := 1 + ((len(f) + 7 + 2) >> 3)

	nullBitmap := p[1:pos]

	var isUnsigned bool
	var isNull bool
	var n int
	var err error
	var v []byte
	for i := range data {
		if nullBitmap[(i+2)/8]&(1<<(uint(i+2)%8)) > 0 {
			data[i] = nil
			continue
		}

		isUnsigned = f[i].Flag&UNSIGNED_FLAG > 0

		switch f[i].Type {
		case MYSQL_TYPE_NULL:
			data[i] = nil
			continue

		case MYSQL_TYPE_TINY:
			if isUnsigned {
				data[i] = uint64(p[pos])
			} else {
				data[i] = int64(p[pos])
			}
			pos++
			continue

		case MYSQL_TYPE_SHORT, MYSQL_TYPE_YEAR:
			if isUnsigned {
				data[i] = uint64(binary.LittleEndian.Uint16(p[pos : pos+2]))
			} else {
				data[i] = int64((binary.LittleEndian.Uint16(p[pos : pos+2])))
			}
			pos += 2
			continue

		case MYSQL_TYPE_INT24, MYSQL_TYPE_LONG:
			if isUnsigned {
				data[i] = uint64(binary.LittleEndian.Uint32(p[pos : pos+4]))
			} else {
				data[i] = int64(binary.LittleEndian.Uint32(p[pos : pos+4]))
			}
			pos += 4
			continue

		case MYSQL_TYPE_LONGLONG:
			if isUnsigned {
				data[i] = binary.LittleEndian.Uint64(p[pos : pos+8])
			} else {
				data[i] = int64(binary.LittleEndian.Uint64(p[pos : pos+8]))
			}
			pos += 8
			continue

		case MYSQL_TYPE_FLOAT:
			data[i] = float64(math.Float32frombits(binary.LittleEndian.Uint32(p[pos : pos+4])))
			pos += 4
			continue

		case MYSQL_TYPE_DOUBLE:
			data[i] = math.Float64frombits(binary.LittleEndian.Uint64(p[pos : pos+8]))
			pos += 8
			continue

		case MYSQL_TYPE_DECIMAL, MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR,
			MYSQL_TYPE_BIT, MYSQL_TYPE_ENUM, MYSQL_TYPE_SET, MYSQL_TYPE_TINY_BLOB,
			MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_BLOB,
			MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY:
			v, isNull, n, err = LengthEnodedString(p[pos:])
			pos += n
			if err != nil {
				return nil, err
			}

			if !isNull {
				data[i] = v
				continue
			} else {
				data[i] = nil
				continue
			}
		case MYSQL_TYPE_DATE, MYSQL_TYPE_NEWDATE:
			var num uint64
			num, isNull, n = LengthEncodedInt(p[pos:])

			pos += n

			if isNull {
				data[i] = nil
				continue
			}

			data[i], err = FormatBinaryDate(int(num), p[pos:])
			pos += int(num)

			if err != nil {
				return nil, err
			}

		case MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_DATETIME:
			var num uint64
			num, isNull, n = LengthEncodedInt(p[pos:])

			pos += n

			if isNull {
				data[i] = nil
				continue
			}

			data[i], err = FormatBinaryDateTime(int(num), p[pos:])
			pos += int(num)

			if err != nil {
				return nil, err
			}

		case MYSQL_TYPE_TIME:
			var num uint64
			num, isNull, n = LengthEncodedInt(p[pos:])

			pos += n

			if isNull {
				data[i] = nil
				continue
			}

			data[i], err = FormatBinaryTime(int(num), p[pos:])
			pos += int(num)

			if err != nil {
				return nil, err
			}

		default:
			return nil, fmt.Errorf("Stmt Unknown FieldType %d %s", f[i].Type, f[i].Name)
		}
	}

	return data, nil
}

type Resultset struct {
	Fields     []*Field
	FieldNames map[string]int
	Values     []RowValue

	RowDatas []RowData
}

func (r *Resultset) RowNumber() int {
	return len(r.Values)
}

func (r *Resultset) ColumnNumber() int {
	return len(r.Fields)
}

func (r *Resultset) GetValue(row, column int) (interface{}, error) {
	if row >= len(r.Values) || row < 0 {
		return nil, fmt.Errorf("invalid row index %d", row)
	}

	if column >= len(r.Fields) || column < 0 {
		return nil, fmt.Errorf("invalid column index %d", column)
	}

	return r.Values[row][column], nil
}

func (r *Resultset) NameIndex(name string) (int, error) {
	if column, ok := r.FieldNames[name]; ok {
		return column, nil
	} else {
		return 0, fmt.Errorf("invalid field name %s", name)
	}
}

func (r *Resultset) GetValueByName(row int, name string) (interface{}, error) {
	if column, err := r.NameIndex(name); err != nil {
		return nil, err
	} else {
		return r.GetValue(row, column)
	}
}

func (r *Resultset) IsNull(row, column int) (bool, error) {
	d, err := r.GetValue(row, column)
	if err != nil {
		return false, err
	}

	return d == nil, nil
}

func (r *Resultset) IsNullByName(row int, name string) (bool, error) {
	if column, err := r.NameIndex(name); err != nil {
		return false, err
	} else {
		return r.IsNull(row, column)
	}
}

func (r *Resultset) GetUint(row, column int) (uint64, error) {
	d, err := r.GetValue(row, column)
	if err != nil {
		return 0, err
	}

	switch v := d.(type) {
	case uint64:
		return v, nil
	case int64:
		return uint64(v), nil
	case float64:
		return uint64(v), nil
	case string:
		return strconv.ParseUint(v, 10, 64)
	case []byte:
		return strconv.ParseUint(string(v), 10, 64)
	case nil:
		return 0, nil
	default:
		return 0, fmt.Errorf("data type is %T", v)
	}
}

func (r *Resultset) GetUintByName(row int, name string) (uint64, error) {
	if column, err := r.NameIndex(name); err != nil {
		return 0, err
	} else {
		return r.GetUint(row, column)
	}
}

func (r *Resultset) GetInt(row, column int) (int64, error) {
	v, err := r.GetUint(row, column)
	if err != nil {
		return 0, err
	}

	return int64(v), nil
}

func (r *Resultset) GetIntByName(row int, name string) (int64, error) {
	v, err := r.GetUintByName(row, name)
	if err != nil {
		return 0, err
	}

	return int64(v), nil
}

func (r *Resultset) GetFloat(row, column int) (float64, error) {
	d, err := r.GetValue(row, column)
	if err != nil {
		return 0, err
	}

	switch v := d.(type) {
	case float64:
		return v, nil
	case uint64:
		return float64(v), nil
	case int64:
		return float64(v), nil
	case string:
		return strconv.ParseFloat(v, 64)
	case []byte:
		return strconv.ParseFloat(string(v), 64)
	case nil:
		return 0, nil
	default:
		return 0, fmt.Errorf("data type is %T", v)
	}
}

func (r *Resultset) GetFloatByName(row int, name string) (float64, error) {
	if column, err := r.NameIndex(name); err != nil {
		return 0, err
	} else {
		return r.GetFloat(row, column)
	}
}

func (r *Resultset) GetString(row, column int) (string, error) {
	d, err := r.GetValue(row, column)
	if err != nil {
		return "", err
	}

	switch v := d.(type) {
	case string:
		return v, nil
	case []byte:
		return hack.String(v), nil
	case int64:
		return strconv.FormatInt(v, 10), nil
	case uint64:
		return strconv.FormatUint(v, 10), nil
	case float64:
		return strconv.FormatFloat(v, 'f', -1, 64), nil
	case nil:
		return "", nil
	default:
		return "", fmt.Errorf("data type is %T", v)
	}
}

func (r *Resultset) GetStringByName(row int, name string) (string, error) {
	if column, err := r.NameIndex(name); err != nil {
		return "", err
	} else {
		return r.GetString(row, column)
	}
}
<|endoftext|>


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

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"log"
	"strings"
	"unicode/utf8"
)

type messageFn func(message []string,
	sourceSocket *websocket.Conn,
	state SignalBox) (newState SignalBox, err error)

func announce(message []string,
	sourceSocket *websocket.Conn,
	state SignalBox) (newState SignalBox, err error) {

	source, destination, err := ParsePeerAndRoom(message)
	if err != nil {
		return state, err
	}

	peer, exists := state.Peers[source.Id]
	if !exists {
		log.Printf("INFO - Adding Peer: %s\n", source.Id)
		state.Peers[source.Id] = new(Peer)
		state.Peers[source.Id].Id = source.Id
		state.Peers[source.Id].socket = sourceSocket // Inject a reference to the websocket within the new peer.
		peer = state.Peers[source.Id]
	}

	room, exists := state.Rooms[destination.Room]
	if !exists {
		log.Printf("INFO - Adding Room: %s\n", destination.Room)
		state.Rooms[destination.Room] = new(Room)
		state.Rooms[destination.Room].Room = destination.Room
		room = state.Rooms[destination.Room]
	}

	if state.PeerIsIn[peer.Id] == nil {
		state.PeerIsIn[peer.Id] = make(map[string]*Room)
	}
	state.PeerIsIn[peer.Id][room.Room] = room

	if state.RoomContains[room.Room] == nil {
		state.RoomContains[room.Room] = make(map[string]*Peer)
	}
	state.RoomContains[room.Room][peer.Id] = peer

	// Annouce the arrival to all the peers currently in the room.
	for _, p := range state.RoomContains[room.Room] {
		if p.Id != peer.Id && p.socket != nil {
			writeMessage(p.socket, message)
		}
	}

	// Report back to the announcer the number of peers in the room.
	members := fmt.Sprintf("{\"memberCount\":%d}", len(state.RoomContains[room.Room]))
	writeMessage(sourceSocket, []string{"/roominfo", members})

	return state, nil
}

func leave(message []string,
	sourceSocket *websocket.Conn,
	state SignalBox) (newState SignalBox, err error) {

	source, destination, err := ParsePeerAndRoom(message)
	if err != nil {
		return state, err
	}

	peer, exists := state.Peers[source.Id]
	if !exists {
		return state, errors.New(fmt.Sprintf("Unable to leave, peer %s doesn't exist", source.Id))
	}

	room, exists := state.Rooms[destination.Room]
	if !exists {
		return state, errors.New(fmt.Sprintf("Unable to leave, room %s doesn't exist", destination.Room))
	}

	return removePeer(peer, room, message, state)
}

func closePeer(message []string,
	sourceSocket *websocket.Conn,
	state SignalBox) (newState SignalBox, err error) {

	source := findPeerBySocket(sourceSocket, state)
	if source == nil {
		return state, errors.New("Unable to close - no Peer matching socket.")
	}

	// Announce to everyone that the peer belonging to sourceSocket
	// has closed and bailed out of their rooms.
	for _, r := range state.PeerIsIn[source.Id] {
		for _, p := range state.RoomContains[r.Room] {
			if p.Id != source.Id && p.socket != nil {
				src := fmt.Sprintf("{\"id\":\"%s\"}", source.Id)
				rm := fmt.Sprintf("{\"room\":\"%s\"}", r.Room)

				state, err = removePeer(source, r, []string{"/leave", src, rm}, state)
				if err != nil {
					return state, err
				}
			}
		}
	}

	// Make sure the socket is closed from this end.
	sourceSocket.Close()

	return state, nil
}

func removePeer(source *Peer, destination *Room, message []string, state SignalBox) (newState SignalBox, err error) {
	delete(state.PeerIsIn[source.Id], destination.Room)
	if len(state.PeerIsIn[source.Id]) == 0 {
		log.Printf("INFO - Removing Peer: %s\n", source.Id)
		delete(state.Peers, source.Id)
		delete(state.PeerIsIn, source.Id)
	}

	delete(state.RoomContains[destination.Room], source.Id)
	if len(state.RoomContains[destination.Room]) == 0 {
		log.Printf("INFO - Removing Room: %s\n", destination.Room)
		delete(state.Rooms, destination.Room)
		delete(state.RoomContains, destination.Room)
	} else {
		// Broadcast the departure to everyone else still in the room
		for _, p := range state.RoomContains[destination.Room] {
			if p.socket != nil {
				writeMessage(p.socket, message)
			}
		}
	}

	return state, nil
}

func to(message []string,
	sourceSocket *websocket.Conn,
	state SignalBox) (newState SignalBox, err error) {

	if len(message) < 3 {
		return state, errors.New("Not enouth parts for personalised 'to' message")
	}

	d, exists := state.Peers[message[1]]
	if !exists {
		return state, nil
	}

	if d.socket != nil {
		writeMessage(d.socket, message)
	}

	return state, nil
}

func writeMessage(ws *websocket.Conn, message []string) {
	b := strings.Join(message, "|")
	if ws != nil {
		log.Printf("INFO - Writing %s to %p", b, ws)
		ws.WriteMessage(websocket.TextMessage, []byte(b))
	}
}

func custom(message []string,
	sourceSocket *websocket.Conn,
	state SignalBox) (newState SignalBox, err error) {

	source := Peer{}
	if len(message) < 2 {
		return state, errors.New("Not enough parts to custom message")
	}

	err = json.Unmarshal([]byte(message[1]), &source)
	if err != nil {
		return state, err
	}

	peer, exists := state.Peers[source.Id]
	if !exists {
		return state, nil
	}

	for _, r := range state.PeerIsIn[peer.Id] {
		for _, p := range state.RoomContains[r.Room] {
			if p.Id != peer.Id && p.socket != nil {
				writeMessage(p.socket, message)
			}
		}
	}

	return state, nil
}

func ignore(message []string,
	sourceSocket *websocket.Conn,
	state SignalBox) (newState SignalBox, err error) {
	return state, nil
}

func findPeerBySocket(sourceSocket *websocket.Conn, state SignalBox) *Peer {
	for _, p := range state.Peers {
		if p.socket == sourceSocket {
			return p
		}
	}

	return nil
}

func ParsePeerAndRoom(message []string) (source Peer, destination Room, err error) {
	if len(message) < 3 {
		return Peer{}, Room{}, errors.New("Not enough parts in the message body to parse peer and room.")
	}

	err = json.Unmarshal([]byte(message[1]), &source)
	if err != nil {
		log.Print(err)
		return Peer{}, Room{}, err
	}

	err = json.Unmarshal([]byte(message[2]), &destination)
	if err != nil {
		return Peer{}, Room{}, err
	}

	return source, destination, nil
}

func ParseMessage(message string) (action messageFn, messageBody []string, err error) {
	// All messages are text (utf-8 encoded at present)
	if !utf8.Valid([]byte(message)) {
		return nil, nil, errors.New("Message is not utf-8 encoded")
	}

	parts := strings.Split(message, "|")

	// rtc.io commands start with "/" - ignore everything else.
	if len(message) > 0 && message[0:1] == "/" {
		switch parts[0] {
		case "/announce":
			return announce, parts, nil

		case "/leave":
			return leave, parts, nil

		case "/to":
			return to, parts, nil

		case "/close":
			return closePeer, parts, nil

		default:
			return custom, parts, nil
		}
	}

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

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"log"
	"strings"
	"unicode/utf8"
)

type messageFn func(message []string,
	sourceSocket *websocket.Conn,
	state SignalBox) (newState SignalBox, err error)

func announce(message []string,
	sourceSocket *websocket.Conn,
	state SignalBox) (newState SignalBox, err error) {

	source, destination, err := ParsePeerAndRoom(message)
	if err != nil {
		return state, err
	}

	peer, exists := state.Peers[source.Id]
	if !exists {
		log.Printf("INFO - Adding Peer: %s\n", source.Id)
		state.Peers[source.Id] = new(Peer)
		state.Peers[source.Id].Id = source.Id
		state.Peers[source.Id].socket = sourceSocket // Inject a reference to the websocket within the new peer.
		peer = state.Peers[source.Id]
	}

	room, exists := state.Rooms[destination.Room]
	if !exists {
		log.Printf("INFO - Adding Room: %s\n", destination.Room)
		state.Rooms[destination.Room] = new(Room)
		state.Rooms[destination.Room].Room = destination.Room
		room = state.Rooms[destination.Room]
	}

	if state.PeerIsIn[peer.Id] == nil {
		state.PeerIsIn[peer.Id] = make(map[string]*Room)
	}
	state.PeerIsIn[peer.Id][room.Room] = room

	if state.RoomContains[room.Room] == nil {
		state.RoomContains[room.Room] = make(map[string]*Peer)
	}
	state.RoomContains[room.Room][peer.Id] = peer

	// Annouce the arrival to all the peers currently in the room.
	for _, p := range state.RoomContains[room.Room] {
		if p.Id != peer.Id && p.socket != nil {
			writeMessage(p.socket, message)
		}
	}

	// Report back to the announcer the number of peers in the room.
	members := fmt.Sprintf("{\"memberCount\":%d}", len(state.RoomContains[room.Room]))
	err = writeMessage(sourceSocket, []string{"/roominfo", members})

	return state, nil
}

func leave(message []string,
	sourceSocket *websocket.Conn,
	state SignalBox) (newState SignalBox, err error) {

	source, destination, err := ParsePeerAndRoom(message)
	if err != nil {
		return state, err
	}

	peer, exists := state.Peers[source.Id]
	if !exists {
		return state, errors.New(fmt.Sprintf("Unable to leave, peer %s doesn't exist", source.Id))
	}

	room, exists := state.Rooms[destination.Room]
	if !exists {
		return state, errors.New(fmt.Sprintf("Unable to leave, room %s doesn't exist", destination.Room))
	}

	return removePeer(peer, room, message, state)
}

func closePeer(message []string,
	sourceSocket *websocket.Conn,
	state SignalBox) (newState SignalBox, err error) {

	source := findPeerBySocket(sourceSocket, state)
	if source == nil {
		return state, errors.New("Unable to close - no Peer matching socket.")
	}

	// Announce to everyone that the peer belonging to sourceSocket
	// has closed and bailed out of their rooms.
	for _, r := range state.PeerIsIn[source.Id] {
		for _, p := range state.RoomContains[r.Room] {
			if p.Id != source.Id && p.socket != nil {
				src := fmt.Sprintf("{\"id\":\"%s\"}", source.Id)
				rm := fmt.Sprintf("{\"room\":\"%s\"}", r.Room)

				state, err = removePeer(source, r, []string{"/leave", src, rm}, state)
				if err != nil {
					return state, err
				}
			}
		}
	}

	// Make sure the socket is closed from this end.
	err = sourceSocket.Close()

	return state, err
}

func removePeer(source *Peer, destination *Room, message []string, state SignalBox) (newState SignalBox, err error) {
	delete(state.PeerIsIn[source.Id], destination.Room)
	if len(state.PeerIsIn[source.Id]) == 0 {
		log.Printf("INFO - Removing Peer: %s\n", source.Id)
		delete(state.Peers, source.Id)
		delete(state.PeerIsIn, source.Id)
	}

	delete(state.RoomContains[destination.Room], source.Id)
	if len(state.RoomContains[destination.Room]) == 0 {
		log.Printf("INFO - Removing Room: %s\n", destination.Room)
		delete(state.Rooms, destination.Room)
		delete(state.RoomContains, destination.Room)
	} else {
		// Broadcast the departure to everyone else still in the room
		for _, p := range state.RoomContains[destination.Room] {
			if p.socket != nil && err == nil {
				err = writeMessage(p.socket, message)
			}
		}
	}

	return state, err
}

func to(message []string,
	sourceSocket *websocket.Conn,
	state SignalBox) (newState SignalBox, err error) {

	if len(message) < 3 {
		return state, errors.New("Not enouth parts for personalised 'to' message")
	}

	d, exists := state.Peers[message[1]]
	if !exists {
		return state, nil
	}

	if d.socket != nil {
		err = writeMessage(d.socket, message)
	}

	return state, err
}

func writeMessage(ws *websocket.Conn, message []string) error {
	b := strings.Join(message, "|")
	if ws != nil {
		log.Printf("INFO - Writing %s to %p", b, ws)
		return ws.WriteMessage(websocket.TextMessage, []byte(b))
	}

	return nil
}

func custom(message []string,
	sourceSocket *websocket.Conn,
	state SignalBox) (newState SignalBox, err error) {

	source := Peer{}
	if len(message) < 2 {
		return state, errors.New("Not enough parts to custom message")
	}

	err = json.Unmarshal([]byte(message[1]), &source)
	if err != nil {
		return state, err
	}

	peer, exists := state.Peers[source.Id]
	if !exists {
		return state, nil
	}

	for _, r := range state.PeerIsIn[peer.Id] {
		for _, p := range state.RoomContains[r.Room] {
			if p.Id != peer.Id && p.socket != nil && err != nil {
				err = writeMessage(p.socket, message)
			}
		}
	}

	return state, err
}

func ignore(message []string,
	sourceSocket *websocket.Conn,
	state SignalBox) (newState SignalBox, err error) {
	return state, nil
}

func findPeerBySocket(sourceSocket *websocket.Conn, state SignalBox) *Peer {
	for _, p := range state.Peers {
		if p.socket == sourceSocket {
			return p
		}
	}

	return nil
}

func ParsePeerAndRoom(message []string) (source Peer, destination Room, err error) {
	if len(message) < 3 {
		return Peer{}, Room{}, errors.New("Not enough parts in the message body to parse peer and room.")
	}

	err = json.Unmarshal([]byte(message[1]), &source)
	if err != nil {
		log.Print(err)
		return Peer{}, Room{}, err
	}

	err = json.Unmarshal([]byte(message[2]), &destination)
	if err != nil {
		return Peer{}, Room{}, err
	}

	return source, destination, nil
}

func ParseMessage(message string) (action messageFn, messageBody []string, err error) {
	// All messages are text (utf-8 encoded at present)
	if !utf8.Valid([]byte(message)) {
		return nil, nil, errors.New("Message is not utf-8 encoded")
	}

	parts := strings.Split(message, "|")

	// rtc.io commands start with "/" - ignore everything else.
	if len(message) > 0 && message[0:1] == "/" {
		switch parts[0] {
		case "/announce":
			return announce, parts, nil

		case "/leave":
			return leave, parts, nil

		case "/to":
			return to, parts, nil

		case "/close":
			return closePeer, parts, nil

		default:
			return custom, parts, nil
		}
	}

	return ignore, parts, nil
}
<|endoftext|>


<commit_before>package main

import "github.com/prometheus/client_golang/prometheus"

const namespace = "imgd"

var (
	inFlightGauge = prometheus.NewGauge(prometheus.GaugeOpts{
		Namespace: namespace,
		Subsystem: "http",
		Name:      "in_flight_requests",
		Help:      "A gauge of requests currently being served.",
	})

	requestDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: namespace,
		Subsystem: "http",
		Name:      "request_duration_seconds",
		Help:      "Histogram of the time (in seconds) each HTTP request took.",
		Buckets:   []float64{.001, .005, 0.0075, .01, .025, .1, .5, 1, 5},
	}, []string{"code"})

	responseSize = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: namespace,
		Subsystem: "http",
		Name:      "response_size_bytes",
		Help:      "A histogram of response sizes (in bytes) for requests.",
		Buckets:   []float64{100, 500, 1000, 2500, 5000},
	}, []string{})

	processingDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: namespace,
		Subsystem: "image",
		Name:      "processing_duration_seconds",
		Help:      "Histogram of the time (in seconds) image processing took.",
		Buckets:   []float64{.00025, .0005, 0.001, 0.0025, .005},
	}, []string{"resource"})

	getDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: namespace,
		Subsystem: "texture",
		Name:      "get_duration_seconds",
		Help:      "Histogram of the time (in seconds) each texture GET took.",
		Buckets:   []float64{.1, .25, .5, 1},
	}, []string{"source"})

	cacheDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: namespace,
		Subsystem: "cache",
		Name:      "operation_duration_seconds",
		Help:      "Histogram of the time (in seconds) each cache operation took.",
		Buckets:   []float64{.00005, .0001, .0005, 0.0025, .005, 0.0075, 0.01, 0.1},
	}, []string{"operation"})

	errorCounter = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: namespace,
			Subsystem: "status",
			Name:      "errors",
			Help:      "Error events",
		},
		[]string{"event"},
	)

	cacheCounter = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: namespace,
			Subsystem: "status",
			Name:      "cache",
			Help:      "Cache status",
		},
		[]string{"status"},
	)

	requestCounter = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: namespace,
			Subsystem: "status",
			Name:      "requests",
			Help:      "Resource requests",
		},
		[]string{"resource"},
	)

	// Latency on Get (source of skin) :tick:
	// Total latency for HTTP request (response code) :tick:
	// Latency on cache  (has, puul or add) :tick:
	// Gauge for cache hit, miss :tick:
	// Gauge for request (type) :tick:
	// Latency for processing (type) :tick:
)

func init() {
	prometheus.MustRegister(inFlightGauge)
	prometheus.MustRegister(requestDuration)
	prometheus.MustRegister(responseSize)
	prometheus.MustRegister(processingDuration)
	prometheus.MustRegister(getDuration)
	prometheus.MustRegister(cacheDuration)
	prometheus.MustRegister(errorCounter)
	prometheus.MustRegister(cacheCounter)
	prometheus.MustRegister(requestCounter)
}
<commit_msg>Adjust histogram value for cache timings<commit_after>package main

import "github.com/prometheus/client_golang/prometheus"

const namespace = "imgd"

var (
	inFlightGauge = prometheus.NewGauge(prometheus.GaugeOpts{
		Namespace: namespace,
		Subsystem: "http",
		Name:      "in_flight_requests",
		Help:      "A gauge of requests currently being served.",
	})

	requestDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: namespace,
		Subsystem: "http",
		Name:      "request_duration_seconds",
		Help:      "Histogram of the time (in seconds) each HTTP request took.",
		Buckets:   []float64{.001, .005, 0.0075, .01, .025, .1, .5, 1, 5},
	}, []string{"code"})

	responseSize = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: namespace,
		Subsystem: "http",
		Name:      "response_size_bytes",
		Help:      "A histogram of response sizes (in bytes) for requests.",
		Buckets:   []float64{100, 500, 1000, 2500, 5000},
	}, []string{})

	processingDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: namespace,
		Subsystem: "image",
		Name:      "processing_duration_seconds",
		Help:      "Histogram of the time (in seconds) image processing took.",
		Buckets:   []float64{.00025, .0005, 0.001, 0.0025, .005},
	}, []string{"resource"})

	getDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: namespace,
		Subsystem: "texture",
		Name:      "get_duration_seconds",
		Help:      "Histogram of the time (in seconds) each texture GET took.",
		Buckets:   []float64{.1, .25, .5, 1},
	}, []string{"source"})

	cacheDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: namespace,
		Subsystem: "cache",
		Name:      "operation_duration_seconds",
		Help:      "Histogram of the time (in seconds) each cache operation took.",
		Buckets:   []float64{.0005, .001, 0.0025, .005, 0.0075, 0.01, 0.1},
	}, []string{"operation"})

	errorCounter = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: namespace,
			Subsystem: "status",
			Name:      "errors",
			Help:      "Error events",
		},
		[]string{"event"},
	)

	cacheCounter = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: namespace,
			Subsystem: "status",
			Name:      "cache",
			Help:      "Cache status",
		},
		[]string{"status"},
	)

	requestCounter = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: namespace,
			Subsystem: "status",
			Name:      "requests",
			Help:      "Resource requests",
		},
		[]string{"resource"},
	)

	// Latency on Get (source of skin) :tick:
	// Total latency for HTTP request (response code) :tick:
	// Latency on cache  (has, puul or add) :tick:
	// Gauge for cache hit, miss :tick:
	// Gauge for request (type) :tick:
	// Latency for processing (type) :tick:
)

func init() {
	prometheus.MustRegister(inFlightGauge)
	prometheus.MustRegister(requestDuration)
	prometheus.MustRegister(responseSize)
	prometheus.MustRegister(processingDuration)
	prometheus.MustRegister(getDuration)
	prometheus.MustRegister(cacheDuration)
	prometheus.MustRegister(errorCounter)
	prometheus.MustRegister(cacheCounter)
	prometheus.MustRegister(requestCounter)
}
<|endoftext|>


<commit_before>/**
 * (C) Copyright 2014, Deft Labs
 *
 * 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 dlshared

import "time"
import "fmt"

type MetricsRelayFunction func(string, []Metric)

type metricType int8

const (
	Counter metricType = 0
	Gauge metricType = 1

	CounterStr = "counter"
	GaugeStr = "gauge"
)

type Metric struct {
	Name string
	Type metricType
	Value float64
}

type Metrics struct {
	sourceName string
	quitChannel chan bool
	relayFunc MetricsRelayFunction
	relayPeriodInSecs int
	metricChannel chan *Metric
	ticker *time.Ticker
}

// The relay function is only called if there are metrics to relay.
func NewMetrics(	sourceName string,
					relayFunc MetricsRelayFunction,
					relayPeriodInSecs int,
					metricQueueLength int) *Metrics {

	return &Metrics{
		sourceName : sourceName,
		relayFunc : relayFunc,
		relayPeriodInSecs: relayPeriodInSecs,
		quitChannel : make(chan bool),
		metricChannel : make(chan *Metric, metricQueueLength),
	}
}

// Update the gauge value
func (self *Metrics) Gauge(metricName string, value float64) {
	self.metricChannel <- &Metric{
		Name: metricName,
		Type: Gauge,
		Value: value,
	}
}

// Increases the counter by one.
func (self *Metrics) Count(metricName string) {
	self.metricChannel <- &Metric{
		Name: metricName,
		Type: Counter,
		Value: 1,
	}
}

// Increase the counter
func (self *Metrics) CountWithValue(metricName string, value float64) {
	self.metricChannel <- &Metric{
		Name: metricName,
		Type: Counter,
		Value: value,
	}
}

func (self *Metrics) listenForEvents() {

	metrics := make(map[string]Metric)

    for {
        select {
			case metric := <- self.metricChannel:
				current, found := metrics[metric.Name]
				if !found {
					metrics[metric.Name] = *metric
					continue
				}

				if metric.Type == Counter {
					current.Value = current.Value + metric.Value
				} else {
					// This is a gauge
					current.Value = metric.Value
				}

			case <- self.ticker.C:

				fmt.Println("Ready to relay events:", len(metrics))

				toRelay := make([]Metric, len(metrics))
				for _, v := range metrics {
					if v.Name == "" {
						fmt.Println("WTF - Emtpy name in to relay setup")
					}
					toRelay = append(toRelay, Metric{ Name: v.Name, Type: v.Type, Value: v.Value })
				}

				if len(toRelay) == 0 {
					continue
				}

				go self.relayFunc(self.sourceName, toRelay)

			case <- self.quitChannel:
				self.ticker.Stop()
				return
        }
    }
}

func (self *Metrics) Start() error {

	self.ticker = time.NewTicker(time.Duration(self.relayPeriodInSecs) * time.Second)

	go self.listenForEvents()

	return nil
}

func (self *Metrics) Stop() error {
	self.quitChannel <- true
	return nil
}

// Pass the logger struct to the log metrics.
func NewLogMetrics(logger Logger) *LogMetrics {
	return &LogMetrics{ Logger: logger, enabled: true }
}

// This can be used to print metrics out to a configured logger.
type LogMetrics struct {
	Logger
	enabled bool
}

func (self *LogMetrics) Disable() {
	self.enabled = false
}

func (self *LogMetrics) Enable() {
	self.enabled = true
}

// This logs an info message with the following format: [source: %s - type: %s - metric: %s - value: %f]
func (self *LogMetrics) Log(sourceName string, metrics []Metric) {

	if !self.enabled {
		return
	}

	var typeStr string
	for i := range metrics {
		switch metrics[i].Type {
			case Counter: typeStr = CounterStr
			case Gauge: typeStr = GaugeStr
		}
		self.Logf(Info, "[source: %s - type: %s - metric: %s - value: %f]", sourceName, typeStr, metrics[i].Name, metrics[i].Value)
	}
}

<commit_msg>debug<commit_after>/**
 * (C) Copyright 2014, Deft Labs
 *
 * 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 dlshared

import "time"
import "fmt"

type MetricsRelayFunction func(string, []Metric)

type metricType int8

const (
	Counter metricType = 0
	Gauge metricType = 1

	CounterStr = "counter"
	GaugeStr = "gauge"
)

type Metric struct {
	Name string
	Type metricType
	Value float64
}

type Metrics struct {
	sourceName string
	quitChannel chan bool
	relayFunc MetricsRelayFunction
	relayPeriodInSecs int
	metricChannel chan *Metric
	ticker *time.Ticker
}

// The relay function is only called if there are metrics to relay.
func NewMetrics(	sourceName string,
					relayFunc MetricsRelayFunction,
					relayPeriodInSecs int,
					metricQueueLength int) *Metrics {

	return &Metrics{
		sourceName : sourceName,
		relayFunc : relayFunc,
		relayPeriodInSecs: relayPeriodInSecs,
		quitChannel : make(chan bool),
		metricChannel : make(chan *Metric, metricQueueLength),
	}
}

// Update the gauge value
func (self *Metrics) Gauge(metricName string, value float64) {
	self.metricChannel <- &Metric{
		Name: metricName,
		Type: Gauge,
		Value: value,
	}
}

// Increases the counter by one.
func (self *Metrics) Count(metricName string) {
	self.metricChannel <- &Metric{
		Name: metricName,
		Type: Counter,
		Value: 1,
	}
}

// Increase the counter
func (self *Metrics) CountWithValue(metricName string, value float64) {
	self.metricChannel <- &Metric{
		Name: metricName,
		Type: Counter,
		Value: value,
	}
}

func (self *Metrics) listenForEvents() {

	metrics := make(map[string]Metric)

    for {
        select {
			case metric := <- self.metricChannel:
				current, found := metrics[metric.Name]
				if !found {
					metrics[metric.Name] = *metric
					continue
				}

				if metric.Type == Counter {
					current.Value = current.Value + metric.Value
				} else {
					// This is a gauge
					current.Value = metric.Value
				}

			case <- self.ticker.C:

				fmt.Println("Ready to relay events:", len(metrics))

				toRelay := make([]Metric, len(metrics))
				for _, v := range metrics {
					if v.Name == "" {
						fmt.Println("WTF - Emtpy name in to relay setup")
					}
					toRelay = append(toRelay, Metric{ Name: v.Name, Type: v.Type, Value: v.Value })
				}

				fmt.Println("Slice size after setup:", len(toRelay))

				if len(toRelay) == 0 {
					continue
				}

				go self.relayFunc(self.sourceName, toRelay)

			case <- self.quitChannel:
				self.ticker.Stop()
				return
        }
    }
}

func (self *Metrics) Start() error {

	self.ticker = time.NewTicker(time.Duration(self.relayPeriodInSecs) * time.Second)

	go self.listenForEvents()

	return nil
}

func (self *Metrics) Stop() error {
	self.quitChannel <- true
	return nil
}

// Pass the logger struct to the log metrics.
func NewLogMetrics(logger Logger) *LogMetrics {
	return &LogMetrics{ Logger: logger, enabled: true }
}

// This can be used to print metrics out to a configured logger.
type LogMetrics struct {
	Logger
	enabled bool
}

func (self *LogMetrics) Disable() {
	self.enabled = false
}

func (self *LogMetrics) Enable() {
	self.enabled = true
}

// This logs an info message with the following format: [source: %s - type: %s - metric: %s - value: %f]
func (self *LogMetrics) Log(sourceName string, metrics []Metric) {

	if !self.enabled {
		return
	}

	var typeStr string
	for i := range metrics {
		switch metrics[i].Type {
			case Counter: typeStr = CounterStr
			case Gauge: typeStr = GaugeStr
		}
		self.Logf(Info, "[source: %s - type: %s - metric: %s - value: %f]", sourceName, typeStr, metrics[i].Name, metrics[i].Value)
	}
}

<|endoftext|>


<commit_before>package pcp

import "hash/fnv"

// MetricType is an enumerated type representing all valid types for a metric
type MetricType int32

// Possible values for a MetricType
const (
	NoSupportType       MetricType = iota
	Int32Type           MetricType = iota
	Uint32Type          MetricType = iota
	Int64Type           MetricType = iota
	Uint64Type          MetricType = iota
	FloatType           MetricType = iota
	DoubleType          MetricType = iota
	StringType          MetricType = iota
	AggregateType       MetricType = iota
	AggregateStaticType MetricType = iota
	EventType           MetricType = iota
	HighresEventType    MetricType = iota
	UnknownType         MetricType = iota
)

func (mt MetricType) String() string {
	switch mt {
	case NoSupportType:
		return "Type: No Support"
	case Int32Type:
		return "Type: Int32"
	case Int64Type:
		return "Type: Int64"
	case Uint32Type:
		return "Type: Uint32"
	case Uint64Type:
		return "Type: Uint64"
	case FloatType:
		return "Type: Float"
	case DoubleType:
		return "Type: Double"
	case StringType:
		return "Type: String"
	case AggregateType:
		return "Type: Aggregate"
	case AggregateStaticType:
		return "Type: Aggregate Static"
	case EventType:
		return "Type: Event"
	case HighresEventType:
		return "Type: Highres Event"
	case UnknownType:
		return "Type: Unknown"
	default:
		return "Type: Invalid"
	}
}

// MetricUnit is an enumerated type representing all possible values for a valid PCP unit
type MetricUnit int32

// SpaceUnit is an enumerated type representing all units for space
type SpaceUnit MetricUnit

// Possible values for SpaceUnit
const (
	ByteUnit     SpaceUnit = iota
	KilobyteUnit SpaceUnit = iota
	MegabyteUnit SpaceUnit = iota
	GigabyteUnit SpaceUnit = iota
	TerabyteUnit SpaceUnit = iota
	PetabyteUnit SpaceUnit = iota
	ExabyteUnit  SpaceUnit = iota
)

func (su SpaceUnit) String() string {
	switch su {
	case ByteUnit:
		return "Unit: Byte"
	case KilobyteUnit:
		return "Unit: Kilobyte"
	case MegabyteUnit:
		return "Unit: Megabyte"
	case GigabyteUnit:
		return "Unit: Gigabyte"
	case TerabyteUnit:
		return "Unit: Terabyte"
	case PetabyteUnit:
		return "Unit: Petabyte"
	case ExabyteUnit:
		return "Unit: Exabyte"
	default:
		return "Unit: Invalid SpaceUnit"
	}
}

// TimeUnit is an enumerated type representing all possible units for representing time
type TimeUnit MetricUnit

// Possible Values for TimeUnit
const (
	NanosecondUnit  TimeUnit = iota
	MicrosecondUnit TimeUnit = iota
	MillisecondUnit TimeUnit = iota
	SecondUnit      TimeUnit = iota
	MinuteUnit      TimeUnit = iota
	HourUnit        TimeUnit = iota
)

func (tu TimeUnit) String() string {
	switch tu {
	case NanosecondUnit:
		return "Unit: Nanosecond"
	case MicrosecondUnit:
		return "Unit: Microsecond"
	case MillisecondUnit:
		return "Unit: Millisecond"
	case SecondUnit:
		return "Unit: Second"
	case MinuteUnit:
		return "Unit: Minute"
	case HourUnit:
		return "Unit: Hour"
	default:
		return "Unit: Invalid TimeUnit"
	}
}

// CountUnit is a type representing a counted quantity
type CountUnit MetricUnit

// OneUnit represents the only CountUnit
const OneUnit CountUnit = iota

func (cu CountUnit) String() string {
	switch cu {
	case OneUnit:
		return "Unit: One"
	default:
		return "Unit: Invalid CounterUnit"
	}
}

// MetricSemantics represents an enumerated type representing the possible
// values for the semantics of a metric
type MetricSemantics int32

// Possible values for MetricSemantics
const (
	NoSemantics       MetricSemantics = iota
	CounterSemantics  MetricSemantics = iota
	InstantSemantics  MetricSemantics = iota
	DiscreteSemantics MetricSemantics = iota
)

func (ms MetricSemantics) String() string {
	switch ms {
	case NoSemantics:
		return "Semantics: None"
	case CounterSemantics:
		return "Semantics: Counter"
	case InstantSemantics:
		return "Semantics: Instant"
	case DiscreteSemantics:
		return "Semantics: Discrete"
	default:
		return "Semantics: Invalid"
	}
}

// Metric defines the general interface a type needs to implement to qualify
// as a valid PCP metric
type Metric interface {
	Val() interface{}           // gets the value of the metric
	Set(interface{}) error      // sets the value of the metric to a value, optionally returns an error on failure
	Type() MetricType           // gets the type of a metric
	Unit() MetricUnit           // gets the unit of a metric
	Semantics() MetricSemantics // gets the semantics for a metric
	Description() string        // gets the description of a metric
}

// generate a unique uint32 hash for a string
// NOTE: make sure this is as fast as possible
func getHash(s string) uint32 {
	h := fnv.New32a()
	h.Write([]byte(s))
	return h.Sum32()
}
<commit_msg>metrics: create MetricDesc type<commit_after>package pcp

import (
	"fmt"
	"hash/fnv"
)

// MetricType is an enumerated type representing all valid types for a metric
type MetricType int32

// Possible values for a MetricType
const (
	NoSupportType       MetricType = iota
	Int32Type           MetricType = iota
	Uint32Type          MetricType = iota
	Int64Type           MetricType = iota
	Uint64Type          MetricType = iota
	FloatType           MetricType = iota
	DoubleType          MetricType = iota
	StringType          MetricType = iota
	AggregateType       MetricType = iota
	AggregateStaticType MetricType = iota
	EventType           MetricType = iota
	HighresEventType    MetricType = iota
	UnknownType         MetricType = iota
)

func (mt MetricType) String() string {
	switch mt {
	case NoSupportType:
		return "Type: No Support"
	case Int32Type:
		return "Type: Int32"
	case Int64Type:
		return "Type: Int64"
	case Uint32Type:
		return "Type: Uint32"
	case Uint64Type:
		return "Type: Uint64"
	case FloatType:
		return "Type: Float"
	case DoubleType:
		return "Type: Double"
	case StringType:
		return "Type: String"
	case AggregateType:
		return "Type: Aggregate"
	case AggregateStaticType:
		return "Type: Aggregate Static"
	case EventType:
		return "Type: Event"
	case HighresEventType:
		return "Type: Highres Event"
	case UnknownType:
		return "Type: Unknown"
	default:
		return "Type: Invalid"
	}
}

// MetricUnit is an enumerated type representing all possible values for a valid PCP unit
type MetricUnit int32

// SpaceUnit is an enumerated type representing all units for space
type SpaceUnit MetricUnit

// Possible values for SpaceUnit
const (
	ByteUnit     SpaceUnit = iota
	KilobyteUnit SpaceUnit = iota
	MegabyteUnit SpaceUnit = iota
	GigabyteUnit SpaceUnit = iota
	TerabyteUnit SpaceUnit = iota
	PetabyteUnit SpaceUnit = iota
	ExabyteUnit  SpaceUnit = iota
)

func (su SpaceUnit) String() string {
	switch su {
	case ByteUnit:
		return "Unit: Byte"
	case KilobyteUnit:
		return "Unit: Kilobyte"
	case MegabyteUnit:
		return "Unit: Megabyte"
	case GigabyteUnit:
		return "Unit: Gigabyte"
	case TerabyteUnit:
		return "Unit: Terabyte"
	case PetabyteUnit:
		return "Unit: Petabyte"
	case ExabyteUnit:
		return "Unit: Exabyte"
	default:
		return "Unit: Invalid SpaceUnit"
	}
}

// TimeUnit is an enumerated type representing all possible units for representing time
type TimeUnit MetricUnit

// Possible Values for TimeUnit
const (
	NanosecondUnit  TimeUnit = iota
	MicrosecondUnit TimeUnit = iota
	MillisecondUnit TimeUnit = iota
	SecondUnit      TimeUnit = iota
	MinuteUnit      TimeUnit = iota
	HourUnit        TimeUnit = iota
)

func (tu TimeUnit) String() string {
	switch tu {
	case NanosecondUnit:
		return "Unit: Nanosecond"
	case MicrosecondUnit:
		return "Unit: Microsecond"
	case MillisecondUnit:
		return "Unit: Millisecond"
	case SecondUnit:
		return "Unit: Second"
	case MinuteUnit:
		return "Unit: Minute"
	case HourUnit:
		return "Unit: Hour"
	default:
		return "Unit: Invalid TimeUnit"
	}
}

// CountUnit is a type representing a counted quantity
type CountUnit MetricUnit

// OneUnit represents the only CountUnit
const OneUnit CountUnit = iota

func (cu CountUnit) String() string {
	switch cu {
	case OneUnit:
		return "Unit: One"
	default:
		return "Unit: Invalid CounterUnit"
	}
}

// MetricSemantics represents an enumerated type representing the possible
// values for the semantics of a metric
type MetricSemantics int32

// Possible values for MetricSemantics
const (
	NoSemantics       MetricSemantics = iota
	CounterSemantics  MetricSemantics = iota
	InstantSemantics  MetricSemantics = iota
	DiscreteSemantics MetricSemantics = iota
)

func (ms MetricSemantics) String() string {
	switch ms {
	case NoSemantics:
		return "Semantics: None"
	case CounterSemantics:
		return "Semantics: Counter"
	case InstantSemantics:
		return "Semantics: Instant"
	case DiscreteSemantics:
		return "Semantics: Discrete"
	default:
		return "Semantics: Invalid"
	}
}

// Metric defines the general interface a type needs to implement to qualify
// as a valid PCP metric
type Metric interface {
	Val() interface{}           // gets the value of the metric
	Set(interface{}) error      // sets the value of the metric to a value, optionally returns an error on failure
	Type() MetricType           // gets the type of a metric
	Unit() MetricUnit           // gets the unit of a metric
	Semantics() MetricSemantics // gets the semantics for a metric
	Description() string        // gets the description of a metric
}

// generate a unique uint32 hash for a string
// NOTE: make sure this is as fast as possible
func getHash(s string) uint32 {
	h := fnv.New32a()
	h.Write([]byte(s))
	return h.Sum32()
}

// MetricDesc is a metric metadata wrapper
// each metric type can wrap its metadata by containing a MetricDesc type and only define its own
// specific properties assuming MetricDesc will handle the rest
//
// when writing, this type is supposed to map directly to the pmDesc struct as defined in PCP core
type MetricDesc struct {
	id                                uint32          // unique metric id
	name                              string          // the name
	indom                             InstanceDomain  // the instance domain
	t                                 MetricType      // the type of a metric
	sem                               MetricSemantics // the semantics
	u                                 MetricUnit      // the unit
	shortDescription, longDescription string
}

// NewMetricDesc creates a new Metric Description wrapper type
func NewMetricDesc(name string, indom InstanceDomain, t MetricType, semantics MetricSemantics, unit MetricUnit, short, long string) *MetricDesc {
	return &MetricDesc{
		getHash(name), name, indom, t, semantics, unit, short, long,
	}
}

func (md *MetricDesc) String() string {
	return fmt.Sprintf("%s{%v, %v, %v, %v}", md.name, md.indom, md.t, md.sem, md.u)
}
<|endoftext|>