mirror of
https://github.com/minio/minio.git
synced 2025-01-22 20:23:14 -05:00
Add NATS notifier (#2795)
This commit is contained in:
parent
64083b9227
commit
1e6afac3bd
@ -150,9 +150,10 @@ func isMinioSNSConfigured(topicARN string, topicConfigs []topicConfig) bool {
|
|||||||
// Validate if we recognize the queue type.
|
// Validate if we recognize the queue type.
|
||||||
func isValidQueue(sqsARN arnSQS) bool {
|
func isValidQueue(sqsARN arnSQS) bool {
|
||||||
amqpQ := isAMQPQueue(sqsARN) // Is amqp queue?.
|
amqpQ := isAMQPQueue(sqsARN) // Is amqp queue?.
|
||||||
|
natsQ := isNATSQueue(sqsARN) // Is nats queue?.
|
||||||
elasticQ := isElasticQueue(sqsARN) // Is elastic queue?.
|
elasticQ := isElasticQueue(sqsARN) // Is elastic queue?.
|
||||||
redisQ := isRedisQueue(sqsARN) // Is redis queue?.
|
redisQ := isRedisQueue(sqsARN) // Is redis queue?.
|
||||||
return amqpQ || elasticQ || redisQ
|
return amqpQ || natsQ || elasticQ || redisQ
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate if we recognize the topic type.
|
// Validate if we recognize the topic type.
|
||||||
@ -168,6 +169,9 @@ func isValidQueueID(queueARN string) bool {
|
|||||||
if isAMQPQueue(sqsARN) { // AMQP eueue.
|
if isAMQPQueue(sqsARN) { // AMQP eueue.
|
||||||
amqpN := serverConfig.GetAMQPNotifyByID(sqsARN.AccountID)
|
amqpN := serverConfig.GetAMQPNotifyByID(sqsARN.AccountID)
|
||||||
return amqpN.Enable && amqpN.URL != ""
|
return amqpN.Enable && amqpN.URL != ""
|
||||||
|
} else if isNATSQueue(sqsARN) {
|
||||||
|
natsN := serverConfig.GetNATSNotifyByID(sqsARN.AccountID)
|
||||||
|
return natsN.Enable && natsN.Address != ""
|
||||||
} else if isElasticQueue(sqsARN) { // Elastic queue.
|
} else if isElasticQueue(sqsARN) { // Elastic queue.
|
||||||
elasticN := serverConfig.GetElasticSearchNotifyByID(sqsARN.AccountID)
|
elasticN := serverConfig.GetElasticSearchNotifyByID(sqsARN.AccountID)
|
||||||
return elasticN.Enable && elasticN.URL != ""
|
return elasticN.Enable && elasticN.URL != ""
|
||||||
@ -347,6 +351,7 @@ func unmarshalTopicARN(topicARN string) arnTopic {
|
|||||||
// Unmarshals input value of AWS ARN format into minioSqs object.
|
// Unmarshals input value of AWS ARN format into minioSqs object.
|
||||||
// Returned value represents minio sqs types, currently supported are
|
// Returned value represents minio sqs types, currently supported are
|
||||||
// - amqp
|
// - amqp
|
||||||
|
// - nats
|
||||||
// - elasticsearch
|
// - elasticsearch
|
||||||
// - redis
|
// - redis
|
||||||
func unmarshalSqsARN(queueARN string) (mSqs arnSQS) {
|
func unmarshalSqsARN(queueARN string) (mSqs arnSQS) {
|
||||||
@ -358,6 +363,8 @@ func unmarshalSqsARN(queueARN string) (mSqs arnSQS) {
|
|||||||
switch {
|
switch {
|
||||||
case strings.HasSuffix(sqsType, queueTypeAMQP):
|
case strings.HasSuffix(sqsType, queueTypeAMQP):
|
||||||
mSqs.Type = queueTypeAMQP
|
mSqs.Type = queueTypeAMQP
|
||||||
|
case strings.HasSuffix(sqsType, queueTypeNATS):
|
||||||
|
mSqs.Type = queueTypeNATS
|
||||||
case strings.HasSuffix(sqsType, queueTypeElastic):
|
case strings.HasSuffix(sqsType, queueTypeElastic):
|
||||||
mSqs.Type = queueTypeElastic
|
mSqs.Type = queueTypeElastic
|
||||||
case strings.HasSuffix(sqsType, queueTypeRedis):
|
case strings.HasSuffix(sqsType, queueTypeRedis):
|
||||||
|
@ -50,6 +50,10 @@ func migrateConfig() error {
|
|||||||
if err := migrateV6ToV7(); err != nil {
|
if err := migrateV6ToV7(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Migrate version '7' to '8'.
|
||||||
|
if err := migrateV7ToV8(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,3 +382,73 @@ func migrateV6ToV7() error {
|
|||||||
console.Println("Migration from version ‘" + cv6.Version + "’ to ‘" + srvConfig.Version + "’ completed successfully.")
|
console.Println("Migration from version ‘" + cv6.Version + "’ to ‘" + srvConfig.Version + "’ completed successfully.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Version '7' to '8' migrates config, removes previous fields related
|
||||||
|
// to backend types and server address. This change further simplifies
|
||||||
|
// the config for future additions.
|
||||||
|
func migrateV7ToV8() error {
|
||||||
|
cv7, err := loadConfigV7()
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Unable to load config version ‘7’. %v", err)
|
||||||
|
}
|
||||||
|
if cv7.Version != "7" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save only the new fields, ignore the rest.
|
||||||
|
srvConfig := &serverConfigV8{}
|
||||||
|
srvConfig.Version = globalMinioConfigVersion
|
||||||
|
srvConfig.Credential = cv7.Credential
|
||||||
|
srvConfig.Region = cv7.Region
|
||||||
|
if srvConfig.Region == "" {
|
||||||
|
// Region needs to be set for AWS Signature Version 4.
|
||||||
|
srvConfig.Region = "us-east-1"
|
||||||
|
}
|
||||||
|
srvConfig.Logger.Console = cv7.Logger.Console
|
||||||
|
srvConfig.Logger.File = cv7.Logger.File
|
||||||
|
srvConfig.Logger.Syslog = cv7.Logger.Syslog
|
||||||
|
srvConfig.Notify.AMQP = make(map[string]amqpNotify)
|
||||||
|
srvConfig.Notify.NATS = make(map[string]natsNotify)
|
||||||
|
srvConfig.Notify.ElasticSearch = make(map[string]elasticSearchNotify)
|
||||||
|
srvConfig.Notify.Redis = make(map[string]redisNotify)
|
||||||
|
if len(cv7.Notify.AMQP) == 0 {
|
||||||
|
srvConfig.Notify.AMQP["1"] = amqpNotify{}
|
||||||
|
} else {
|
||||||
|
srvConfig.Notify.AMQP = cv7.Notify.AMQP
|
||||||
|
}
|
||||||
|
if len(cv7.Notify.NATS) == 0 {
|
||||||
|
srvConfig.Notify.NATS["1"] = natsNotify{}
|
||||||
|
} else {
|
||||||
|
srvConfig.Notify.NATS = cv7.Notify.NATS
|
||||||
|
}
|
||||||
|
if len(cv7.Notify.ElasticSearch) == 0 {
|
||||||
|
srvConfig.Notify.ElasticSearch["1"] = elasticSearchNotify{}
|
||||||
|
} else {
|
||||||
|
srvConfig.Notify.ElasticSearch = cv7.Notify.ElasticSearch
|
||||||
|
}
|
||||||
|
if len(cv7.Notify.Redis) == 0 {
|
||||||
|
srvConfig.Notify.Redis["1"] = redisNotify{}
|
||||||
|
} else {
|
||||||
|
srvConfig.Notify.Redis = cv7.Notify.Redis
|
||||||
|
}
|
||||||
|
|
||||||
|
qc, err := quick.New(srvConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to initialize the quick config. %v", err)
|
||||||
|
}
|
||||||
|
configFile, err := getConfigFile()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to get config file. %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = qc.Save(configFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to migrate config from ‘"+cv7.Version+"’ to ‘"+srvConfig.Version+"’ failed. %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.Println("Migration from version ‘" + cv7.Version + "’ to ‘" + srvConfig.Version + "’ completed successfully.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
const lastConfigVersion = 7
|
const lastConfigVersion = 8
|
||||||
|
|
||||||
// TestServerConfigMigrateV1 - tests if a config v1 is purged
|
// TestServerConfigMigrateV1 - tests if a config v1 is purged
|
||||||
func TestServerConfigMigrateV1(t *testing.T) {
|
func TestServerConfigMigrateV1(t *testing.T) {
|
||||||
@ -90,10 +90,14 @@ func TestServerConfigMigrateInexistentConfig(t *testing.T) {
|
|||||||
if err := migrateV6ToV7(); err != nil {
|
if err := migrateV6ToV7(); err != nil {
|
||||||
t.Fatal("migrate v6 to v7 should succeed when no config file is found")
|
t.Fatal("migrate v6 to v7 should succeed when no config file is found")
|
||||||
}
|
}
|
||||||
|
if err := migrateV7ToV8(); err != nil {
|
||||||
|
t.Fatal("migrate v7 to v8 should succeed when no config file is found")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestServerConfigMigrateV2toV7 - tests if a config from v2 to v7 is successfully done
|
// TestServerConfigMigrateV2toV8 - tests if a config from v2 to v8 is successfully done
|
||||||
func TestServerConfigMigrateV2toV7(t *testing.T) {
|
func TestServerConfigMigrateV2toV8(t *testing.T) {
|
||||||
rootPath, err := newTestConfig("us-east-1")
|
rootPath, err := newTestConfig("us-east-1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Init Test config failed")
|
t.Fatalf("Init Test config failed")
|
||||||
@ -184,5 +188,7 @@ func TestServerConfigMigrateFaultyConfig(t *testing.T) {
|
|||||||
if err := migrateV6ToV7(); err == nil {
|
if err := migrateV6ToV7(); err == nil {
|
||||||
t.Fatal("migrateConfigV6ToV7() should fail with a corrupted json")
|
t.Fatal("migrateConfigV6ToV7() should fail with a corrupted json")
|
||||||
}
|
}
|
||||||
|
if err := migrateV7ToV8(); err == nil {
|
||||||
|
t.Fatal("migrateConfigV7ToV8() should fail with a corrupted json")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/quick"
|
"github.com/minio/minio/pkg/quick"
|
||||||
)
|
)
|
||||||
@ -310,3 +311,42 @@ func loadConfigV6() (*configV6, error) {
|
|||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// configV7 server configuration version '7'.
|
||||||
|
type serverConfigV7 struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
|
||||||
|
// S3 API configuration.
|
||||||
|
Credential credential `json:"credential"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
|
||||||
|
// Additional error logging configuration.
|
||||||
|
Logger logger `json:"logger"`
|
||||||
|
|
||||||
|
// Notification queue configuration.
|
||||||
|
Notify notifier `json:"notify"`
|
||||||
|
|
||||||
|
// Read Write mutex.
|
||||||
|
rwMutex *sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadConfigV7 load config version '7'.
|
||||||
|
func loadConfigV7() (*serverConfigV7, error) {
|
||||||
|
configFile, err := getConfigFile()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err = os.Stat(configFile); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c := &serverConfigV7{}
|
||||||
|
c.Version = "7"
|
||||||
|
qc, err := quick.New(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := qc.Load(configFile); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
@ -23,8 +23,8 @@ import (
|
|||||||
"github.com/minio/minio/pkg/quick"
|
"github.com/minio/minio/pkg/quick"
|
||||||
)
|
)
|
||||||
|
|
||||||
// serverConfigV7 server configuration version '7'.
|
// serverConfigV8 server configuration version '8'.
|
||||||
type serverConfigV7 struct {
|
type serverConfigV8 struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
|
|
||||||
// S3 API configuration.
|
// S3 API configuration.
|
||||||
@ -45,7 +45,7 @@ type serverConfigV7 struct {
|
|||||||
func initConfig() error {
|
func initConfig() error {
|
||||||
if !isConfigFileExists() {
|
if !isConfigFileExists() {
|
||||||
// Initialize server config.
|
// Initialize server config.
|
||||||
srvCfg := &serverConfigV7{}
|
srvCfg := &serverConfigV8{}
|
||||||
srvCfg.Version = globalMinioConfigVersion
|
srvCfg.Version = globalMinioConfigVersion
|
||||||
srvCfg.Region = "us-east-1"
|
srvCfg.Region = "us-east-1"
|
||||||
srvCfg.Credential = mustGenAccessKeys()
|
srvCfg.Credential = mustGenAccessKeys()
|
||||||
@ -63,6 +63,9 @@ func initConfig() error {
|
|||||||
srvCfg.Notify.ElasticSearch["1"] = elasticSearchNotify{}
|
srvCfg.Notify.ElasticSearch["1"] = elasticSearchNotify{}
|
||||||
srvCfg.Notify.Redis = make(map[string]redisNotify)
|
srvCfg.Notify.Redis = make(map[string]redisNotify)
|
||||||
srvCfg.Notify.Redis["1"] = redisNotify{}
|
srvCfg.Notify.Redis["1"] = redisNotify{}
|
||||||
|
srvCfg.Notify.NATS = make(map[string]natsNotify)
|
||||||
|
srvCfg.Notify.NATS["1"] = natsNotify{}
|
||||||
|
|
||||||
srvCfg.rwMutex = &sync.RWMutex{}
|
srvCfg.rwMutex = &sync.RWMutex{}
|
||||||
|
|
||||||
// Create config path.
|
// Create config path.
|
||||||
@ -84,7 +87,7 @@ func initConfig() error {
|
|||||||
if _, err = os.Stat(configFile); err != nil {
|
if _, err = os.Stat(configFile); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
srvCfg := &serverConfigV7{}
|
srvCfg := &serverConfigV8{}
|
||||||
srvCfg.Version = globalMinioConfigVersion
|
srvCfg.Version = globalMinioConfigVersion
|
||||||
srvCfg.rwMutex = &sync.RWMutex{}
|
srvCfg.rwMutex = &sync.RWMutex{}
|
||||||
qc, err := quick.New(srvCfg)
|
qc, err := quick.New(srvCfg)
|
||||||
@ -103,10 +106,10 @@ func initConfig() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// serverConfig server config.
|
// serverConfig server config.
|
||||||
var serverConfig *serverConfigV7
|
var serverConfig *serverConfigV8
|
||||||
|
|
||||||
// GetVersion get current config version.
|
// GetVersion get current config version.
|
||||||
func (s serverConfigV7) GetVersion() string {
|
func (s serverConfigV8) GetVersion() string {
|
||||||
s.rwMutex.RLock()
|
s.rwMutex.RLock()
|
||||||
defer s.rwMutex.RUnlock()
|
defer s.rwMutex.RUnlock()
|
||||||
return s.Version
|
return s.Version
|
||||||
@ -114,135 +117,155 @@ func (s serverConfigV7) GetVersion() string {
|
|||||||
|
|
||||||
/// Logger related.
|
/// Logger related.
|
||||||
|
|
||||||
func (s *serverConfigV7) SetAMQPNotifyByID(accountID string, amqpn amqpNotify) {
|
func (s *serverConfigV8) SetAMQPNotifyByID(accountID string, amqpn amqpNotify) {
|
||||||
s.rwMutex.Lock()
|
s.rwMutex.Lock()
|
||||||
defer s.rwMutex.Unlock()
|
defer s.rwMutex.Unlock()
|
||||||
s.Notify.AMQP[accountID] = amqpn
|
s.Notify.AMQP[accountID] = amqpn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s serverConfigV7) GetAMQP() map[string]amqpNotify {
|
func (s serverConfigV8) GetAMQP() map[string]amqpNotify {
|
||||||
s.rwMutex.RLock()
|
s.rwMutex.RLock()
|
||||||
defer s.rwMutex.RUnlock()
|
defer s.rwMutex.RUnlock()
|
||||||
return s.Notify.AMQP
|
return s.Notify.AMQP
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAMQPNotify get current AMQP logger.
|
// GetAMQPNotify get current AMQP logger.
|
||||||
func (s serverConfigV7) GetAMQPNotifyByID(accountID string) amqpNotify {
|
func (s serverConfigV8) GetAMQPNotifyByID(accountID string) amqpNotify {
|
||||||
s.rwMutex.RLock()
|
s.rwMutex.RLock()
|
||||||
defer s.rwMutex.RUnlock()
|
defer s.rwMutex.RUnlock()
|
||||||
return s.Notify.AMQP[accountID]
|
return s.Notify.AMQP[accountID]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serverConfigV7) SetElasticSearchNotifyByID(accountID string, esNotify elasticSearchNotify) {
|
//
|
||||||
|
func (s *serverConfigV8) SetNATSNotifyByID(accountID string, natsn natsNotify) {
|
||||||
|
s.rwMutex.Lock()
|
||||||
|
defer s.rwMutex.Unlock()
|
||||||
|
s.Notify.NATS[accountID] = natsn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s serverConfigV8) GetNATS() map[string]natsNotify {
|
||||||
|
s.rwMutex.RLock()
|
||||||
|
defer s.rwMutex.RUnlock()
|
||||||
|
return s.Notify.NATS
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNATSNotify get current NATS logger.
|
||||||
|
func (s serverConfigV8) GetNATSNotifyByID(accountID string) natsNotify {
|
||||||
|
s.rwMutex.RLock()
|
||||||
|
defer s.rwMutex.RUnlock()
|
||||||
|
return s.Notify.NATS[accountID]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serverConfigV8) SetElasticSearchNotifyByID(accountID string, esNotify elasticSearchNotify) {
|
||||||
s.rwMutex.Lock()
|
s.rwMutex.Lock()
|
||||||
defer s.rwMutex.Unlock()
|
defer s.rwMutex.Unlock()
|
||||||
s.Notify.ElasticSearch[accountID] = esNotify
|
s.Notify.ElasticSearch[accountID] = esNotify
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s serverConfigV7) GetElasticSearch() map[string]elasticSearchNotify {
|
func (s serverConfigV8) GetElasticSearch() map[string]elasticSearchNotify {
|
||||||
s.rwMutex.RLock()
|
s.rwMutex.RLock()
|
||||||
defer s.rwMutex.RUnlock()
|
defer s.rwMutex.RUnlock()
|
||||||
return s.Notify.ElasticSearch
|
return s.Notify.ElasticSearch
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetElasticSearchNotify get current ElasicSearch logger.
|
// GetElasticSearchNotify get current ElasicSearch logger.
|
||||||
func (s serverConfigV7) GetElasticSearchNotifyByID(accountID string) elasticSearchNotify {
|
func (s serverConfigV8) GetElasticSearchNotifyByID(accountID string) elasticSearchNotify {
|
||||||
s.rwMutex.RLock()
|
s.rwMutex.RLock()
|
||||||
defer s.rwMutex.RUnlock()
|
defer s.rwMutex.RUnlock()
|
||||||
return s.Notify.ElasticSearch[accountID]
|
return s.Notify.ElasticSearch[accountID]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serverConfigV7) SetRedisNotifyByID(accountID string, rNotify redisNotify) {
|
func (s *serverConfigV8) SetRedisNotifyByID(accountID string, rNotify redisNotify) {
|
||||||
s.rwMutex.Lock()
|
s.rwMutex.Lock()
|
||||||
defer s.rwMutex.Unlock()
|
defer s.rwMutex.Unlock()
|
||||||
s.Notify.Redis[accountID] = rNotify
|
s.Notify.Redis[accountID] = rNotify
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s serverConfigV7) GetRedis() map[string]redisNotify {
|
func (s serverConfigV8) GetRedis() map[string]redisNotify {
|
||||||
s.rwMutex.RLock()
|
s.rwMutex.RLock()
|
||||||
defer s.rwMutex.RUnlock()
|
defer s.rwMutex.RUnlock()
|
||||||
return s.Notify.Redis
|
return s.Notify.Redis
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRedisNotify get current Redis logger.
|
// GetRedisNotify get current Redis logger.
|
||||||
func (s serverConfigV7) GetRedisNotifyByID(accountID string) redisNotify {
|
func (s serverConfigV8) GetRedisNotifyByID(accountID string) redisNotify {
|
||||||
s.rwMutex.RLock()
|
s.rwMutex.RLock()
|
||||||
defer s.rwMutex.RUnlock()
|
defer s.rwMutex.RUnlock()
|
||||||
return s.Notify.Redis[accountID]
|
return s.Notify.Redis[accountID]
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFileLogger set new file logger.
|
// SetFileLogger set new file logger.
|
||||||
func (s *serverConfigV7) SetFileLogger(flogger fileLogger) {
|
func (s *serverConfigV8) SetFileLogger(flogger fileLogger) {
|
||||||
s.rwMutex.Lock()
|
s.rwMutex.Lock()
|
||||||
defer s.rwMutex.Unlock()
|
defer s.rwMutex.Unlock()
|
||||||
s.Logger.File = flogger
|
s.Logger.File = flogger
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFileLogger get current file logger.
|
// GetFileLogger get current file logger.
|
||||||
func (s serverConfigV7) GetFileLogger() fileLogger {
|
func (s serverConfigV8) GetFileLogger() fileLogger {
|
||||||
s.rwMutex.RLock()
|
s.rwMutex.RLock()
|
||||||
defer s.rwMutex.RUnlock()
|
defer s.rwMutex.RUnlock()
|
||||||
return s.Logger.File
|
return s.Logger.File
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetConsoleLogger set new console logger.
|
// SetConsoleLogger set new console logger.
|
||||||
func (s *serverConfigV7) SetConsoleLogger(clogger consoleLogger) {
|
func (s *serverConfigV8) SetConsoleLogger(clogger consoleLogger) {
|
||||||
s.rwMutex.Lock()
|
s.rwMutex.Lock()
|
||||||
defer s.rwMutex.Unlock()
|
defer s.rwMutex.Unlock()
|
||||||
s.Logger.Console = clogger
|
s.Logger.Console = clogger
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConsoleLogger get current console logger.
|
// GetConsoleLogger get current console logger.
|
||||||
func (s serverConfigV7) GetConsoleLogger() consoleLogger {
|
func (s serverConfigV8) GetConsoleLogger() consoleLogger {
|
||||||
s.rwMutex.RLock()
|
s.rwMutex.RLock()
|
||||||
defer s.rwMutex.RUnlock()
|
defer s.rwMutex.RUnlock()
|
||||||
return s.Logger.Console
|
return s.Logger.Console
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSyslogLogger set new syslog logger.
|
// SetSyslogLogger set new syslog logger.
|
||||||
func (s *serverConfigV7) SetSyslogLogger(slogger syslogLogger) {
|
func (s *serverConfigV8) SetSyslogLogger(slogger syslogLogger) {
|
||||||
s.rwMutex.Lock()
|
s.rwMutex.Lock()
|
||||||
defer s.rwMutex.Unlock()
|
defer s.rwMutex.Unlock()
|
||||||
s.Logger.Syslog = slogger
|
s.Logger.Syslog = slogger
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSyslogLogger get current syslog logger.
|
// GetSyslogLogger get current syslog logger.
|
||||||
func (s *serverConfigV7) GetSyslogLogger() syslogLogger {
|
func (s *serverConfigV8) GetSyslogLogger() syslogLogger {
|
||||||
s.rwMutex.RLock()
|
s.rwMutex.RLock()
|
||||||
defer s.rwMutex.RUnlock()
|
defer s.rwMutex.RUnlock()
|
||||||
return s.Logger.Syslog
|
return s.Logger.Syslog
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRegion set new region.
|
// SetRegion set new region.
|
||||||
func (s *serverConfigV7) SetRegion(region string) {
|
func (s *serverConfigV8) SetRegion(region string) {
|
||||||
s.rwMutex.Lock()
|
s.rwMutex.Lock()
|
||||||
defer s.rwMutex.Unlock()
|
defer s.rwMutex.Unlock()
|
||||||
s.Region = region
|
s.Region = region
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRegion get current region.
|
// GetRegion get current region.
|
||||||
func (s serverConfigV7) GetRegion() string {
|
func (s serverConfigV8) GetRegion() string {
|
||||||
s.rwMutex.RLock()
|
s.rwMutex.RLock()
|
||||||
defer s.rwMutex.RUnlock()
|
defer s.rwMutex.RUnlock()
|
||||||
return s.Region
|
return s.Region
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCredentials set new credentials.
|
// SetCredentials set new credentials.
|
||||||
func (s *serverConfigV7) SetCredential(creds credential) {
|
func (s *serverConfigV8) SetCredential(creds credential) {
|
||||||
s.rwMutex.Lock()
|
s.rwMutex.Lock()
|
||||||
defer s.rwMutex.Unlock()
|
defer s.rwMutex.Unlock()
|
||||||
s.Credential = creds
|
s.Credential = creds
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCredentials get current credentials.
|
// GetCredentials get current credentials.
|
||||||
func (s serverConfigV7) GetCredential() credential {
|
func (s serverConfigV8) GetCredential() credential {
|
||||||
s.rwMutex.RLock()
|
s.rwMutex.RLock()
|
||||||
defer s.rwMutex.RUnlock()
|
defer s.rwMutex.RUnlock()
|
||||||
return s.Credential
|
return s.Credential
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save config.
|
// Save config.
|
||||||
func (s serverConfigV7) Save() error {
|
func (s serverConfigV8) Save() error {
|
||||||
s.rwMutex.RLock()
|
s.rwMutex.RLock()
|
||||||
defer s.rwMutex.RUnlock()
|
defer s.rwMutex.RUnlock()
|
||||||
|
|
@ -332,6 +332,34 @@ func loadAllQueueTargets() (map[string]*logrus.Logger, error) {
|
|||||||
}
|
}
|
||||||
queueTargets[queueARN] = amqpLog
|
queueTargets[queueARN] = amqpLog
|
||||||
}
|
}
|
||||||
|
// Load all nats targets, initialize their respective loggers.
|
||||||
|
for accountID, natsN := range serverConfig.GetNATS() {
|
||||||
|
if !natsN.Enable {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Construct the queue ARN for NATS.
|
||||||
|
queueARN := minioSqs + serverConfig.GetRegion() + ":" + accountID + ":" + queueTypeNATS
|
||||||
|
// Queue target if already initialized we move to the next ARN.
|
||||||
|
_, ok := queueTargets[queueARN]
|
||||||
|
if ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Using accountID we can now initialize a new NATS logrus instance.
|
||||||
|
natsLog, err := newNATSNotify(accountID)
|
||||||
|
if err != nil {
|
||||||
|
// Encapsulate network error to be more informative.
|
||||||
|
if _, ok := err.(net.Error); ok {
|
||||||
|
return nil, &net.OpError{
|
||||||
|
Op: "Connecting to " + queueARN,
|
||||||
|
Net: "tcp",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
queueTargets[queueARN] = natsLog
|
||||||
|
}
|
||||||
|
|
||||||
// Load redis targets, initialize their respective loggers.
|
// Load redis targets, initialize their respective loggers.
|
||||||
for accountID, redisN := range serverConfig.GetRedis() {
|
for accountID, redisN := range serverConfig.GetRedis() {
|
||||||
if !redisN.Enable {
|
if !redisN.Enable {
|
||||||
|
@ -30,7 +30,7 @@ const (
|
|||||||
|
|
||||||
// minio configuration related constants.
|
// minio configuration related constants.
|
||||||
const (
|
const (
|
||||||
globalMinioConfigVersion = "7"
|
globalMinioConfigVersion = "8"
|
||||||
globalMinioConfigDir = ".minio"
|
globalMinioConfigDir = ".minio"
|
||||||
globalMinioCertsDir = "certs"
|
globalMinioCertsDir = "certs"
|
||||||
globalMinioCertFile = "public.crt"
|
globalMinioCertFile = "public.crt"
|
||||||
|
@ -30,6 +30,8 @@ const (
|
|||||||
|
|
||||||
// Static string indicating queue type 'amqp'.
|
// Static string indicating queue type 'amqp'.
|
||||||
queueTypeAMQP = "amqp"
|
queueTypeAMQP = "amqp"
|
||||||
|
// Static string indicating queue type 'nats'.
|
||||||
|
queueTypeNATS = "nats"
|
||||||
// Static string indicating queue type 'elasticsearch'.
|
// Static string indicating queue type 'elasticsearch'.
|
||||||
queueTypeElastic = "elasticsearch"
|
queueTypeElastic = "elasticsearch"
|
||||||
// Static string indicating queue type 'redis'.
|
// Static string indicating queue type 'redis'.
|
||||||
@ -50,6 +52,7 @@ var errNotifyNotEnabled = errors.New("requested notifier not enabled")
|
|||||||
// Notifier represents collection of supported notification queues.
|
// Notifier represents collection of supported notification queues.
|
||||||
type notifier struct {
|
type notifier struct {
|
||||||
AMQP map[string]amqpNotify `json:"amqp"`
|
AMQP map[string]amqpNotify `json:"amqp"`
|
||||||
|
NATS map[string]natsNotify `json:"nats"`
|
||||||
ElasticSearch map[string]elasticSearchNotify `json:"elasticsearch"`
|
ElasticSearch map[string]elasticSearchNotify `json:"elasticsearch"`
|
||||||
Redis map[string]redisNotify `json:"redis"`
|
Redis map[string]redisNotify `json:"redis"`
|
||||||
// Add new notification queues.
|
// Add new notification queues.
|
||||||
@ -74,6 +77,25 @@ func isAMQPQueue(sqsArn arnSQS) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if natsArn is for an NATS queue.
|
||||||
|
func isNATSQueue(sqsArn arnSQS) bool {
|
||||||
|
if sqsArn.Type != queueTypeNATS {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
natsL := serverConfig.GetNATSNotifyByID(sqsArn.AccountID)
|
||||||
|
if !natsL.Enable {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Connect to nats server to validate.
|
||||||
|
natsC, err := dialNATS(natsL)
|
||||||
|
if err != nil {
|
||||||
|
errorIf(err, "Unable to connect to nats service. %#v", natsL)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer natsC.Close()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Returns true if queueArn is for an Redis queue.
|
// Returns true if queueArn is for an Redis queue.
|
||||||
func isRedisQueue(sqsArn arnSQS) bool {
|
func isRedisQueue(sqsArn arnSQS) bool {
|
||||||
if sqsArn.Type != queueTypeRedis {
|
if sqsArn.Type != queueTypeRedis {
|
||||||
|
108
cmd/notify-nats.go
Normal file
108
cmd/notify-nats.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2016 Minio, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/nats-io/nats"
|
||||||
|
)
|
||||||
|
|
||||||
|
// natsNotify - represents logrus compatible NATS hook.
|
||||||
|
// All fields represent NATS configuration details.
|
||||||
|
type natsNotify struct {
|
||||||
|
Enable bool `json:"enable"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
Secure bool `json:"secure"`
|
||||||
|
PingInterval int64 `json:"pingInterval"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type natsConn struct {
|
||||||
|
params natsNotify
|
||||||
|
*nats.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// dialNATS - dials and returns an natsConn instance,
|
||||||
|
// for sending notifications. Returns error if nats logger
|
||||||
|
// is not enabled.
|
||||||
|
func dialNATS(natsL natsNotify) (natsConn, error) {
|
||||||
|
if !natsL.Enable {
|
||||||
|
return natsConn{}, errNotifyNotEnabled
|
||||||
|
}
|
||||||
|
// Configure and connect to NATS server
|
||||||
|
natsC := nats.DefaultOptions
|
||||||
|
natsC.Url = "nats://" + natsL.Address
|
||||||
|
natsC.User = natsL.Username
|
||||||
|
natsC.Password = natsL.Password
|
||||||
|
natsC.Token = natsL.Token
|
||||||
|
natsC.Secure = natsL.Secure
|
||||||
|
conn, err := natsC.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return natsConn{}, err
|
||||||
|
}
|
||||||
|
return natsConn{Conn: conn, params: natsL}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNATSNotify(accountID string) (*logrus.Logger, error) {
|
||||||
|
natsL := serverConfig.GetNATSNotifyByID(accountID)
|
||||||
|
|
||||||
|
// Connect to nats server.
|
||||||
|
natsC, err := dialNATS(natsL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
natsLog := logrus.New()
|
||||||
|
|
||||||
|
// Disable writing to console.
|
||||||
|
natsLog.Out = ioutil.Discard
|
||||||
|
|
||||||
|
// Add a nats hook.
|
||||||
|
natsLog.Hooks.Add(natsC)
|
||||||
|
|
||||||
|
// Set default JSON formatter.
|
||||||
|
natsLog.Formatter = new(logrus.JSONFormatter)
|
||||||
|
|
||||||
|
// Successfully enabled all NATSs.
|
||||||
|
return natsLog, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire is called when an event should be sent to the message broker
|
||||||
|
func (n natsConn) Fire(entry *logrus.Entry) error {
|
||||||
|
ch := n.Conn
|
||||||
|
body, err := entry.Reader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ch.Publish(n.params.Subject, body.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Levels is available logging levels.
|
||||||
|
func (n natsConn) Levels() []logrus.Level {
|
||||||
|
return []logrus.Level{
|
||||||
|
logrus.InfoLevel,
|
||||||
|
}
|
||||||
|
}
|
20
vendor/github.com/nats-io/nats/LICENSE
generated
vendored
Normal file
20
vendor/github.com/nats-io/nats/LICENSE
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2012-2016 Apcera Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
322
vendor/github.com/nats-io/nats/README.md
generated
vendored
Normal file
322
vendor/github.com/nats-io/nats/README.md
generated
vendored
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
# NATS - Go Client
|
||||||
|
A [Go](http://golang.org) client for the [NATS messaging system](https://nats.io).
|
||||||
|
|
||||||
|
[![License MIT](https://img.shields.io/npm/l/express.svg)](http://opensource.org/licenses/MIT)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/nats-io/nats)](https://goreportcard.com/report/github.com/nats-io/nats) [![Build Status](https://travis-ci.org/nats-io/nats.svg?branch=master)](http://travis-ci.org/nats-io/nats) [![GoDoc](http://godoc.org/github.com/nats-io/nats?status.png)](http://godoc.org/github.com/nats-io/nats) [![Coverage Status](https://coveralls.io/repos/nats-io/nats/badge.svg?branch=master)](https://coveralls.io/r/nats-io/nats?branch=master)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Go client
|
||||||
|
go get github.com/nats-io/nats
|
||||||
|
|
||||||
|
# Server
|
||||||
|
go get github.com/nats-io/gnatsd
|
||||||
|
```
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
nc, _ := nats.Connect(nats.DefaultURL)
|
||||||
|
|
||||||
|
// Simple Publisher
|
||||||
|
nc.Publish("foo", []byte("Hello World"))
|
||||||
|
|
||||||
|
// Simple Async Subscriber
|
||||||
|
nc.Subscribe("foo", func(m *nats.Msg) {
|
||||||
|
fmt.Printf("Received a message: %s\n", string(m.Data))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Simple Sync Subscriber
|
||||||
|
sub, err := nc.SubscribeSync("foo")
|
||||||
|
m, err := sub.NextMsg(timeout)
|
||||||
|
|
||||||
|
// Channel Subscriber
|
||||||
|
ch := make(chan *nats.Msg, 64)
|
||||||
|
sub, err := nc.ChanSubscribe("foo", ch)
|
||||||
|
msg <- ch
|
||||||
|
|
||||||
|
// Unsubscribe
|
||||||
|
sub.Unsubscribe()
|
||||||
|
|
||||||
|
// Requests
|
||||||
|
msg, err := nc.Request("help", []byte("help me"), 10*time.Millisecond)
|
||||||
|
|
||||||
|
// Replies
|
||||||
|
nc.Subscribe("help", func(m *Msg) {
|
||||||
|
nc.Publish(m.Reply, []byte("I can help!"))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Close connection
|
||||||
|
nc := nats.Connect("nats://localhost:4222")
|
||||||
|
nc.Close();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Encoded Connections
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
nc, _ := nats.Connect(nats.DefaultURL)
|
||||||
|
c, _ := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
// Simple Publisher
|
||||||
|
c.Publish("foo", "Hello World")
|
||||||
|
|
||||||
|
// Simple Async Subscriber
|
||||||
|
c.Subscribe("foo", func(s string) {
|
||||||
|
fmt.Printf("Received a message: %s\n", s)
|
||||||
|
})
|
||||||
|
|
||||||
|
// EncodedConn can Publish any raw Go type using the registered Encoder
|
||||||
|
type person struct {
|
||||||
|
Name string
|
||||||
|
Address string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go type Subscriber
|
||||||
|
c.Subscribe("hello", func(p *person) {
|
||||||
|
fmt.Printf("Received a person: %+v\n", p)
|
||||||
|
})
|
||||||
|
|
||||||
|
me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery Street, San Francisco, CA"}
|
||||||
|
|
||||||
|
// Go type Publisher
|
||||||
|
c.Publish("hello", me)
|
||||||
|
|
||||||
|
// Unsubscribe
|
||||||
|
sub, err := c.Subscribe("foo", nil)
|
||||||
|
...
|
||||||
|
sub.Unsubscribe()
|
||||||
|
|
||||||
|
// Requests
|
||||||
|
var response string
|
||||||
|
err := c.Request("help", "help me", &response, 10*time.Millisecond)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Request failed: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replying
|
||||||
|
c.Subscribe("help", func(subj, reply string, msg string) {
|
||||||
|
c.Publish(reply, "I can help!")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Close connection
|
||||||
|
c.Close();
|
||||||
|
```
|
||||||
|
|
||||||
|
## TLS
|
||||||
|
|
||||||
|
```go
|
||||||
|
// tls as a scheme will enable secure connections by default. This will also verify the server name.
|
||||||
|
nc, err := nats.Connect("tls://nats.demo.io:4443")
|
||||||
|
|
||||||
|
// If you are using a self-signed certificate, you need to have a tls.Config with RootCAs setup.
|
||||||
|
// We provide a helper method to make this case easier.
|
||||||
|
nc, err = nats.Connect("tls://localhost:4443", nats.RootCAs("./configs/certs/ca.pem"))
|
||||||
|
|
||||||
|
// If the server requires client certificate, there is an helper function for that too:
|
||||||
|
cert := nats.ClientCert("./configs/certs/client-cert.pem", "./configs/certs/client-key.pem")
|
||||||
|
nc, err = nats.Connect("tls://localhost:4443", cert)
|
||||||
|
|
||||||
|
// You can also supply a complete tls.Config
|
||||||
|
|
||||||
|
certFile := "./configs/certs/client-cert.pem"
|
||||||
|
keyFile := "./configs/certs/client-key.pem"
|
||||||
|
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error parsing X509 certificate/key pair: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &tls.Config{
|
||||||
|
ServerName: opts.Host,
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
RootCAs: pool,
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
}
|
||||||
|
|
||||||
|
nc, err = nats.Connect("nats://localhost:4443", nats.Secure(config))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using Go Channels (netchan)
|
||||||
|
|
||||||
|
```go
|
||||||
|
nc, _ := nats.Connect(nats.DefaultURL)
|
||||||
|
ec, _ := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
|
||||||
|
defer ec.Close()
|
||||||
|
|
||||||
|
type person struct {
|
||||||
|
Name string
|
||||||
|
Address string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
recvCh := make(chan *person)
|
||||||
|
ec.BindRecvChan("hello", recvCh)
|
||||||
|
|
||||||
|
sendCh := make(chan *person)
|
||||||
|
ec.BindSendChan("hello", sendCh)
|
||||||
|
|
||||||
|
me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery Street"}
|
||||||
|
|
||||||
|
// Send via Go channels
|
||||||
|
sendCh <- me
|
||||||
|
|
||||||
|
// Receive via Go channels
|
||||||
|
who := <- recvCh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Wildcard Subscriptions
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
// "*" matches any token, at any level of the subject.
|
||||||
|
nc.Subscribe("foo.*.baz", func(m *Msg) {
|
||||||
|
fmt.Printf("Msg received on [%s] : %s\n", m.Subject, string(m.Data));
|
||||||
|
})
|
||||||
|
|
||||||
|
nc.Subscribe("foo.bar.*", func(m *Msg) {
|
||||||
|
fmt.Printf("Msg received on [%s] : %s\n", m.Subject, string(m.Data));
|
||||||
|
})
|
||||||
|
|
||||||
|
// ">" matches any length of the tail of a subject, and can only be the last token
|
||||||
|
// E.g. 'foo.>' will match 'foo.bar', 'foo.bar.baz', 'foo.foo.bar.bax.22'
|
||||||
|
nc.Subscribe("foo.>", func(m *Msg) {
|
||||||
|
fmt.Printf("Msg received on [%s] : %s\n", m.Subject, string(m.Data));
|
||||||
|
})
|
||||||
|
|
||||||
|
// Matches all of the above
|
||||||
|
nc.Publish("foo.bar.baz", []byte("Hello World"))
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Queue Groups
|
||||||
|
|
||||||
|
```go
|
||||||
|
// All subscriptions with the same queue name will form a queue group.
|
||||||
|
// Each message will be delivered to only one subscriber per queue group,
|
||||||
|
// using queuing semantics. You can have as many queue groups as you wish.
|
||||||
|
// Normal subscribers will continue to work as expected.
|
||||||
|
|
||||||
|
nc.QueueSubscribe("foo", "job_workers", func(_ *Msg) {
|
||||||
|
received += 1;
|
||||||
|
})
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
// Flush connection to server, returns when all messages have been processed.
|
||||||
|
nc.Flush()
|
||||||
|
fmt.Println("All clear!")
|
||||||
|
|
||||||
|
// FlushTimeout specifies a timeout value as well.
|
||||||
|
err := nc.FlushTimeout(1*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("All clear!")
|
||||||
|
} else {
|
||||||
|
fmt.Println("Flushed timed out!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-unsubscribe after MAX_WANTED messages received
|
||||||
|
const MAX_WANTED = 10
|
||||||
|
sub, err := nc.Subscribe("foo")
|
||||||
|
sub.AutoUnsubscribe(MAX_WANTED)
|
||||||
|
|
||||||
|
// Multiple connections
|
||||||
|
nc1 := nats.Connect("nats://host1:4222")
|
||||||
|
nc2 := nats.Connect("nats://host2:4222")
|
||||||
|
|
||||||
|
nc1.Subscribe("foo", func(m *Msg) {
|
||||||
|
fmt.Printf("Received a message: %s\n", string(m.Data))
|
||||||
|
})
|
||||||
|
|
||||||
|
nc2.Publish("foo", []byte("Hello World!"));
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Clustered Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
var servers = "nats://localhost:1222, nats://localhost:1223, nats://localhost:1224"
|
||||||
|
|
||||||
|
nc, err := nats.Connect(servers)
|
||||||
|
|
||||||
|
// Optionally set ReconnectWait and MaxReconnect attempts.
|
||||||
|
// This example means 10 seconds total per backend.
|
||||||
|
nc, err = nats.Connect(servers, nats.MaxReconnects(5), nats.ReconnectWait(2 * time.Second))
|
||||||
|
|
||||||
|
// Optionally disable randomization of the server pool
|
||||||
|
nc, err = nats.Connect(servers, nats.DontRandomize())
|
||||||
|
|
||||||
|
// Setup callbacks to be notified on disconnects, reconnects and connection closed.
|
||||||
|
nc, err = nats.Connect(servers,
|
||||||
|
nats.DisconnectHandler(func(nc *nats.Conn) {
|
||||||
|
fmt.Printf("Got disconnected!\n")
|
||||||
|
}),
|
||||||
|
nats.ReconnectHandler(func(_ *nats.Conn) {
|
||||||
|
fmt.Printf("Got reconnected to %v!\n", nc.ConnectedUrl())
|
||||||
|
}),
|
||||||
|
nats.ClosedHandler(func(nc *nats.Conn) {
|
||||||
|
fmt.Printf("Connection closed. Reason: %q\n", nc.LastError())
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// When connecting to a mesh of servers with auto-discovery capabilities,
|
||||||
|
// you may need to provide a username/password or token in order to connect
|
||||||
|
// to any server in that mesh when authentication is required.
|
||||||
|
// Instead of providing the credentials in the initial URL, you will use
|
||||||
|
// new option setters:
|
||||||
|
nc, err = nats.Connect("nats://localhost:4222", nats.UserInfo("foo", "bar"))
|
||||||
|
|
||||||
|
// For token based authentication:
|
||||||
|
nc, err = nats.Connect("nats://localhost:4222", nats.Token("S3cretT0ken"))
|
||||||
|
|
||||||
|
// You can even pass the two at the same time in case one of the server
|
||||||
|
// in the mesh requires token instead of user name and password.
|
||||||
|
nc, err = nats.Connect("nats://localhost:4222",
|
||||||
|
nats.UserInfo("foo", "bar"),
|
||||||
|
nats.Token("S3cretT0ken"))
|
||||||
|
|
||||||
|
// Note that if credentials are specified in the initial URLs, they take
|
||||||
|
// precedence on the credentials specfied through the options.
|
||||||
|
// For instance, in the connect call below, the client library will use
|
||||||
|
// the user "my" and password "pwd" to connect to locahost:4222, however,
|
||||||
|
// it will use username "foo" and password "bar" when (re)connecting to
|
||||||
|
// a different server URL that it got as part of the auto-discovery.
|
||||||
|
nc, err = nats.Connect("nats://my:pwd@localhost:4222", nats.UserInfo("foo", "bar"))
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
(The MIT License)
|
||||||
|
|
||||||
|
Copyright (c) 2012-2016 Apcera Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to
|
||||||
|
deal in the Software without restriction, including without limitation the
|
||||||
|
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
IN THE SOFTWARE.
|
26
vendor/github.com/nats-io/nats/TODO.md
generated
vendored
Normal file
26
vendor/github.com/nats-io/nats/TODO.md
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
- [ ] Better constructors, options handling
|
||||||
|
- [ ] Functions for callback settings after connection created.
|
||||||
|
- [ ] Better options for subscriptions. Slow Consumer state settable, Go routines vs Inline.
|
||||||
|
- [ ] Move off of channels for subscribers, use syncPool linkedLists, etc with highwater.
|
||||||
|
- [ ] Test for valid subjects on publish and subscribe?
|
||||||
|
- [ ] SyncSubscriber and Next for EncodedConn
|
||||||
|
- [ ] Fast Publisher?
|
||||||
|
- [ ] pooling for structs used? leaky bucket?
|
||||||
|
- [ ] Timeout 0 should work as no timeout
|
||||||
|
- [x] Ping timer
|
||||||
|
- [x] Name in Connect for gnatsd
|
||||||
|
- [x] Asynchronous error handling
|
||||||
|
- [x] Parser rewrite
|
||||||
|
- [x] Reconnect
|
||||||
|
- [x] Hide Lock
|
||||||
|
- [x] Easier encoder interface
|
||||||
|
- [x] QueueSubscribeSync
|
||||||
|
- [x] Make nats specific errors prefixed with 'nats:'
|
||||||
|
- [x] API test for closed connection
|
||||||
|
- [x] TLS/SSL
|
||||||
|
- [x] Stats collection
|
||||||
|
- [x] Disconnect detection
|
||||||
|
- [x] Optimized Publish (coalescing)
|
||||||
|
- [x] Do Examples via Go style
|
||||||
|
- [x] Standardized Errors
|
249
vendor/github.com/nats-io/nats/enc.go
generated
vendored
Normal file
249
vendor/github.com/nats-io/nats/enc.go
generated
vendored
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
// Copyright 2012-2015 Apcera Inc. All rights reserved.
|
||||||
|
|
||||||
|
package nats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
// Default Encoders
|
||||||
|
. "github.com/nats-io/nats/encoders/builtin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Encoder interface is for all register encoders
|
||||||
|
type Encoder interface {
|
||||||
|
Encode(subject string, v interface{}) ([]byte, error)
|
||||||
|
Decode(subject string, data []byte, vPtr interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var encMap map[string]Encoder
|
||||||
|
var encLock sync.Mutex
|
||||||
|
|
||||||
|
// Indexe names into the Registered Encoders.
|
||||||
|
const (
|
||||||
|
JSON_ENCODER = "json"
|
||||||
|
GOB_ENCODER = "gob"
|
||||||
|
DEFAULT_ENCODER = "default"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
encMap = make(map[string]Encoder)
|
||||||
|
// Register json, gob and default encoder
|
||||||
|
RegisterEncoder(JSON_ENCODER, &JsonEncoder{})
|
||||||
|
RegisterEncoder(GOB_ENCODER, &GobEncoder{})
|
||||||
|
RegisterEncoder(DEFAULT_ENCODER, &DefaultEncoder{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodedConn are the preferred way to interface with NATS. They wrap a bare connection to
|
||||||
|
// a nats server and have an extendable encoder system that will encode and decode messages
|
||||||
|
// from raw Go types.
|
||||||
|
type EncodedConn struct {
|
||||||
|
Conn *Conn
|
||||||
|
Enc Encoder
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncodedConn will wrap an existing Connection and utilize the appropriate registered
|
||||||
|
// encoder.
|
||||||
|
func NewEncodedConn(c *Conn, encType string) (*EncodedConn, error) {
|
||||||
|
if c == nil {
|
||||||
|
return nil, errors.New("nats: Nil Connection")
|
||||||
|
}
|
||||||
|
if c.IsClosed() {
|
||||||
|
return nil, ErrConnectionClosed
|
||||||
|
}
|
||||||
|
ec := &EncodedConn{Conn: c, Enc: EncoderForType(encType)}
|
||||||
|
if ec.Enc == nil {
|
||||||
|
return nil, fmt.Errorf("No encoder registered for '%s'", encType)
|
||||||
|
}
|
||||||
|
return ec, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterEncoder will register the encType with the given Encoder. Useful for customization.
|
||||||
|
func RegisterEncoder(encType string, enc Encoder) {
|
||||||
|
encLock.Lock()
|
||||||
|
defer encLock.Unlock()
|
||||||
|
encMap[encType] = enc
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncoderForType will return the registered Encoder for the encType.
|
||||||
|
func EncoderForType(encType string) Encoder {
|
||||||
|
encLock.Lock()
|
||||||
|
defer encLock.Unlock()
|
||||||
|
return encMap[encType]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish publishes the data argument to the given subject. The data argument
|
||||||
|
// will be encoded using the associated encoder.
|
||||||
|
func (c *EncodedConn) Publish(subject string, v interface{}) error {
|
||||||
|
b, err := c.Enc.Encode(subject, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.Conn.publish(subject, _EMPTY_, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublishRequest will perform a Publish() expecting a response on the
|
||||||
|
// reply subject. Use Request() for automatically waiting for a response
|
||||||
|
// inline.
|
||||||
|
func (c *EncodedConn) PublishRequest(subject, reply string, v interface{}) error {
|
||||||
|
b, err := c.Enc.Encode(subject, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.Conn.publish(subject, reply, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request will create an Inbox and perform a Request() call
|
||||||
|
// with the Inbox reply for the data v. A response will be
|
||||||
|
// decoded into the vPtrResponse.
|
||||||
|
func (c *EncodedConn) Request(subject string, v interface{}, vPtr interface{}, timeout time.Duration) error {
|
||||||
|
b, err := c.Enc.Encode(subject, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m, err := c.Conn.Request(subject, b, timeout)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if reflect.TypeOf(vPtr) == emptyMsgType {
|
||||||
|
mPtr := vPtr.(*Msg)
|
||||||
|
*mPtr = *m
|
||||||
|
} else {
|
||||||
|
err = c.Enc.Decode(m.Subject, m.Data, vPtr)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler is a specific callback used for Subscribe. It is generalized to
|
||||||
|
// an interface{}, but we will discover its format and arguments at runtime
|
||||||
|
// and perform the correct callback, including de-marshalling JSON strings
|
||||||
|
// back into the appropriate struct based on the signature of the Handler.
|
||||||
|
//
|
||||||
|
// Handlers are expected to have one of four signatures.
|
||||||
|
//
|
||||||
|
// type person struct {
|
||||||
|
// Name string `json:"name,omitempty"`
|
||||||
|
// Age uint `json:"age,omitempty"`
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// handler := func(m *Msg)
|
||||||
|
// handler := func(p *person)
|
||||||
|
// handler := func(subject string, o *obj)
|
||||||
|
// handler := func(subject, reply string, o *obj)
|
||||||
|
//
|
||||||
|
// These forms allow a callback to request a raw Msg ptr, where the processing
|
||||||
|
// of the message from the wire is untouched. Process a JSON representation
|
||||||
|
// and demarshal it into the given struct, e.g. person.
|
||||||
|
// There are also variants where the callback wants either the subject, or the
|
||||||
|
// subject and the reply subject.
|
||||||
|
type Handler interface{}
|
||||||
|
|
||||||
|
// Dissect the cb Handler's signature
|
||||||
|
func argInfo(cb Handler) (reflect.Type, int) {
|
||||||
|
cbType := reflect.TypeOf(cb)
|
||||||
|
if cbType.Kind() != reflect.Func {
|
||||||
|
panic("nats: Handler needs to be a func")
|
||||||
|
}
|
||||||
|
numArgs := cbType.NumIn()
|
||||||
|
if numArgs == 0 {
|
||||||
|
return nil, numArgs
|
||||||
|
}
|
||||||
|
return cbType.In(numArgs - 1), numArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
var emptyMsgType = reflect.TypeOf(&Msg{})
|
||||||
|
|
||||||
|
// Subscribe will create a subscription on the given subject and process incoming
|
||||||
|
// messages using the specified Handler. The Handler should be a func that matches
|
||||||
|
// a signature from the description of Handler from above.
|
||||||
|
func (c *EncodedConn) Subscribe(subject string, cb Handler) (*Subscription, error) {
|
||||||
|
return c.subscribe(subject, _EMPTY_, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueueSubscribe will create a queue subscription on the given subject and process
|
||||||
|
// incoming messages using the specified Handler. The Handler should be a func that
|
||||||
|
// matches a signature from the description of Handler from above.
|
||||||
|
func (c *EncodedConn) QueueSubscribe(subject, queue string, cb Handler) (*Subscription, error) {
|
||||||
|
return c.subscribe(subject, queue, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal implementation that all public functions will use.
|
||||||
|
func (c *EncodedConn) subscribe(subject, queue string, cb Handler) (*Subscription, error) {
|
||||||
|
if cb == nil {
|
||||||
|
return nil, errors.New("nats: Handler required for EncodedConn Subscription")
|
||||||
|
}
|
||||||
|
argType, numArgs := argInfo(cb)
|
||||||
|
if argType == nil {
|
||||||
|
return nil, errors.New("nats: Handler requires at least one argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
cbValue := reflect.ValueOf(cb)
|
||||||
|
wantsRaw := (argType == emptyMsgType)
|
||||||
|
|
||||||
|
natsCB := func(m *Msg) {
|
||||||
|
var oV []reflect.Value
|
||||||
|
if wantsRaw {
|
||||||
|
oV = []reflect.Value{reflect.ValueOf(m)}
|
||||||
|
} else {
|
||||||
|
var oPtr reflect.Value
|
||||||
|
if argType.Kind() != reflect.Ptr {
|
||||||
|
oPtr = reflect.New(argType)
|
||||||
|
} else {
|
||||||
|
oPtr = reflect.New(argType.Elem())
|
||||||
|
}
|
||||||
|
if err := c.Enc.Decode(m.Subject, m.Data, oPtr.Interface()); err != nil {
|
||||||
|
if c.Conn.Opts.AsyncErrorCB != nil {
|
||||||
|
c.Conn.ach <- func() {
|
||||||
|
c.Conn.Opts.AsyncErrorCB(c.Conn, m.Sub, errors.New("nats: Got an error trying to unmarshal: "+err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if argType.Kind() != reflect.Ptr {
|
||||||
|
oPtr = reflect.Indirect(oPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback Arity
|
||||||
|
switch numArgs {
|
||||||
|
case 1:
|
||||||
|
oV = []reflect.Value{oPtr}
|
||||||
|
case 2:
|
||||||
|
subV := reflect.ValueOf(m.Subject)
|
||||||
|
oV = []reflect.Value{subV, oPtr}
|
||||||
|
case 3:
|
||||||
|
subV := reflect.ValueOf(m.Subject)
|
||||||
|
replyV := reflect.ValueOf(m.Reply)
|
||||||
|
oV = []reflect.Value{subV, replyV, oPtr}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
cbValue.Call(oV)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Conn.subscribe(subject, queue, natsCB, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlushTimeout allows a Flush operation to have an associated timeout.
|
||||||
|
func (c *EncodedConn) FlushTimeout(timeout time.Duration) (err error) {
|
||||||
|
return c.Conn.FlushTimeout(timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush will perform a round trip to the server and return when it
|
||||||
|
// receives the internal reply.
|
||||||
|
func (c *EncodedConn) Flush() error {
|
||||||
|
return c.Conn.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close will close the connection to the server. This call will release
|
||||||
|
// all blocking calls, such as Flush(), etc.
|
||||||
|
func (c *EncodedConn) Close() {
|
||||||
|
c.Conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastError reports the last error encountered via the Connection.
|
||||||
|
func (c *EncodedConn) LastError() error {
|
||||||
|
return c.Conn.err
|
||||||
|
}
|
106
vendor/github.com/nats-io/nats/encoders/builtin/default_enc.go
generated
vendored
Normal file
106
vendor/github.com/nats-io/nats/encoders/builtin/default_enc.go
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// Copyright 2012-2015 Apcera Inc. All rights reserved.
|
||||||
|
|
||||||
|
package builtin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultEncoder implementation for EncodedConn.
|
||||||
|
// This encoder will leave []byte and string untouched, but will attempt to
|
||||||
|
// turn numbers into appropriate strings that can be decoded. It will also
|
||||||
|
// propely encoded and decode bools. If will encode a struct, but if you want
|
||||||
|
// to properly handle structures you should use JsonEncoder.
|
||||||
|
type DefaultEncoder struct {
|
||||||
|
// Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
var trueB = []byte("true")
|
||||||
|
var falseB = []byte("false")
|
||||||
|
var nilB = []byte("")
|
||||||
|
|
||||||
|
// Encode
|
||||||
|
func (je *DefaultEncoder) Encode(subject string, v interface{}) ([]byte, error) {
|
||||||
|
switch arg := v.(type) {
|
||||||
|
case string:
|
||||||
|
bytes := *(*[]byte)(unsafe.Pointer(&arg))
|
||||||
|
return bytes, nil
|
||||||
|
case []byte:
|
||||||
|
return arg, nil
|
||||||
|
case bool:
|
||||||
|
if arg {
|
||||||
|
return trueB, nil
|
||||||
|
} else {
|
||||||
|
return falseB, nil
|
||||||
|
}
|
||||||
|
case nil:
|
||||||
|
return nilB, nil
|
||||||
|
default:
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fmt.Fprintf(&buf, "%+v", arg)
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode
|
||||||
|
func (je *DefaultEncoder) Decode(subject string, data []byte, vPtr interface{}) error {
|
||||||
|
// Figure out what it's pointing to...
|
||||||
|
sData := *(*string)(unsafe.Pointer(&data))
|
||||||
|
switch arg := vPtr.(type) {
|
||||||
|
case *string:
|
||||||
|
*arg = sData
|
||||||
|
return nil
|
||||||
|
case *[]byte:
|
||||||
|
*arg = data
|
||||||
|
return nil
|
||||||
|
case *int:
|
||||||
|
n, err := strconv.ParseInt(sData, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*arg = int(n)
|
||||||
|
return nil
|
||||||
|
case *int32:
|
||||||
|
n, err := strconv.ParseInt(sData, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*arg = int32(n)
|
||||||
|
return nil
|
||||||
|
case *int64:
|
||||||
|
n, err := strconv.ParseInt(sData, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*arg = int64(n)
|
||||||
|
return nil
|
||||||
|
case *float32:
|
||||||
|
n, err := strconv.ParseFloat(sData, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*arg = float32(n)
|
||||||
|
return nil
|
||||||
|
case *float64:
|
||||||
|
n, err := strconv.ParseFloat(sData, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*arg = float64(n)
|
||||||
|
return nil
|
||||||
|
case *bool:
|
||||||
|
b, err := strconv.ParseBool(sData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*arg = b
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
vt := reflect.TypeOf(arg).Elem()
|
||||||
|
return fmt.Errorf("nats: Default Encoder can't decode to type %s", vt)
|
||||||
|
}
|
||||||
|
}
|
34
vendor/github.com/nats-io/nats/encoders/builtin/gob_enc.go
generated
vendored
Normal file
34
vendor/github.com/nats-io/nats/encoders/builtin/gob_enc.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2013-2015 Apcera Inc. All rights reserved.
|
||||||
|
|
||||||
|
package builtin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/gob"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GobEncoder is a Go specific GOB Encoder implementation for EncodedConn.
|
||||||
|
// This encoder will use the builtin encoding/gob to Marshal
|
||||||
|
// and Unmarshal most types, including structs.
|
||||||
|
type GobEncoder struct {
|
||||||
|
// Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(dlc) - This could probably be more efficient.
|
||||||
|
|
||||||
|
// Encode
|
||||||
|
func (ge *GobEncoder) Encode(subject string, v interface{}) ([]byte, error) {
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
enc := gob.NewEncoder(b)
|
||||||
|
if err := enc.Encode(v); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode
|
||||||
|
func (ge *GobEncoder) Decode(subject string, data []byte, vPtr interface{}) (err error) {
|
||||||
|
dec := gob.NewDecoder(bytes.NewBuffer(data))
|
||||||
|
err = dec.Decode(vPtr)
|
||||||
|
return
|
||||||
|
}
|
45
vendor/github.com/nats-io/nats/encoders/builtin/json_enc.go
generated
vendored
Normal file
45
vendor/github.com/nats-io/nats/encoders/builtin/json_enc.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2012-2015 Apcera Inc. All rights reserved.
|
||||||
|
|
||||||
|
package builtin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JsonEncoder is a JSON Encoder implementation for EncodedConn.
|
||||||
|
// This encoder will use the builtin encoding/json to Marshal
|
||||||
|
// and Unmarshal most types, including structs.
|
||||||
|
type JsonEncoder struct {
|
||||||
|
// Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode
|
||||||
|
func (je *JsonEncoder) Encode(subject string, v interface{}) ([]byte, error) {
|
||||||
|
b, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode
|
||||||
|
func (je *JsonEncoder) Decode(subject string, data []byte, vPtr interface{}) (err error) {
|
||||||
|
switch arg := vPtr.(type) {
|
||||||
|
case *string:
|
||||||
|
// If they want a string and it is a JSON string, strip quotes
|
||||||
|
// This allows someone to send a struct but receive as a plain string
|
||||||
|
// This cast should be efficient for Go 1.3 and beyond.
|
||||||
|
str := string(data)
|
||||||
|
if strings.HasPrefix(str, `"`) && strings.HasSuffix(str, `"`) {
|
||||||
|
*arg = str[1 : len(str)-1]
|
||||||
|
} else {
|
||||||
|
*arg = str
|
||||||
|
}
|
||||||
|
case *[]byte:
|
||||||
|
*arg = data
|
||||||
|
default:
|
||||||
|
err = json.Unmarshal(data, arg)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
2571
vendor/github.com/nats-io/nats/nats.go
generated
vendored
Normal file
2571
vendor/github.com/nats-io/nats/nats.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
100
vendor/github.com/nats-io/nats/netchan.go
generated
vendored
Normal file
100
vendor/github.com/nats-io/nats/netchan.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
// Copyright 2013-2014 Apcera Inc. All rights reserved.
|
||||||
|
|
||||||
|
package nats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This allows the functionality for network channels by binding send and receive Go chans
|
||||||
|
// to subjects and optionally queue groups.
|
||||||
|
// Data will be encoded and decoded via the EncodedConn and its associated encoders.
|
||||||
|
|
||||||
|
// BindSendChan binds a channel for send operations to NATS.
|
||||||
|
func (c *EncodedConn) BindSendChan(subject string, channel interface{}) error {
|
||||||
|
chVal := reflect.ValueOf(channel)
|
||||||
|
if chVal.Kind() != reflect.Chan {
|
||||||
|
return ErrChanArg
|
||||||
|
}
|
||||||
|
go chPublish(c, chVal, subject)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish all values that arrive on the channel until it is closed or we
|
||||||
|
// encounter an error.
|
||||||
|
func chPublish(c *EncodedConn, chVal reflect.Value, subject string) {
|
||||||
|
for {
|
||||||
|
val, ok := chVal.Recv()
|
||||||
|
if !ok {
|
||||||
|
// Channel has most likely been closed.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e := c.Publish(subject, val.Interface()); e != nil {
|
||||||
|
// Do this under lock.
|
||||||
|
c.Conn.mu.Lock()
|
||||||
|
defer c.Conn.mu.Unlock()
|
||||||
|
|
||||||
|
if c.Conn.Opts.AsyncErrorCB != nil {
|
||||||
|
// FIXME(dlc) - Not sure this is the right thing to do.
|
||||||
|
// FIXME(ivan) - If the connection is not yet closed, try to schedule the callback
|
||||||
|
if c.Conn.isClosed() {
|
||||||
|
go c.Conn.Opts.AsyncErrorCB(c.Conn, nil, e)
|
||||||
|
} else {
|
||||||
|
c.Conn.ach <- func() { c.Conn.Opts.AsyncErrorCB(c.Conn, nil, e) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindRecvChan binds a channel for receive operations from NATS.
|
||||||
|
func (c *EncodedConn) BindRecvChan(subject string, channel interface{}) (*Subscription, error) {
|
||||||
|
return c.bindRecvChan(subject, _EMPTY_, channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindRecvQueueChan binds a channel for queue-based receive operations from NATS.
|
||||||
|
func (c *EncodedConn) BindRecvQueueChan(subject, queue string, channel interface{}) (*Subscription, error) {
|
||||||
|
return c.bindRecvChan(subject, queue, channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal function to bind receive operations for a channel.
|
||||||
|
func (c *EncodedConn) bindRecvChan(subject, queue string, channel interface{}) (*Subscription, error) {
|
||||||
|
chVal := reflect.ValueOf(channel)
|
||||||
|
if chVal.Kind() != reflect.Chan {
|
||||||
|
return nil, ErrChanArg
|
||||||
|
}
|
||||||
|
argType := chVal.Type().Elem()
|
||||||
|
|
||||||
|
cb := func(m *Msg) {
|
||||||
|
var oPtr reflect.Value
|
||||||
|
if argType.Kind() != reflect.Ptr {
|
||||||
|
oPtr = reflect.New(argType)
|
||||||
|
} else {
|
||||||
|
oPtr = reflect.New(argType.Elem())
|
||||||
|
}
|
||||||
|
if err := c.Enc.Decode(m.Subject, m.Data, oPtr.Interface()); err != nil {
|
||||||
|
c.Conn.err = errors.New("nats: Got an error trying to unmarshal: " + err.Error())
|
||||||
|
if c.Conn.Opts.AsyncErrorCB != nil {
|
||||||
|
c.Conn.ach <- func() { c.Conn.Opts.AsyncErrorCB(c.Conn, m.Sub, c.Conn.err) }
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if argType.Kind() != reflect.Ptr {
|
||||||
|
oPtr = reflect.Indirect(oPtr)
|
||||||
|
}
|
||||||
|
// This is a bit hacky, but in this instance we may be trying to send to a closed channel.
|
||||||
|
// and the user does not know when it is safe to close the channel.
|
||||||
|
defer func() {
|
||||||
|
// If we have panicked, recover and close the subscription.
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
m.Sub.Unsubscribe()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// Actually do the send to the channel.
|
||||||
|
chVal.Send(oPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Conn.subscribe(subject, queue, cb, nil)
|
||||||
|
}
|
470
vendor/github.com/nats-io/nats/parser.go
generated
vendored
Normal file
470
vendor/github.com/nats-io/nats/parser.go
generated
vendored
Normal file
@ -0,0 +1,470 @@
|
|||||||
|
// Copyright 2012-2014 Apcera Inc. All rights reserved.
|
||||||
|
|
||||||
|
package nats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type msgArg struct {
|
||||||
|
subject []byte
|
||||||
|
reply []byte
|
||||||
|
sid int64
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_CONTROL_LINE_SIZE = 1024
|
||||||
|
|
||||||
|
type parseState struct {
|
||||||
|
state int
|
||||||
|
as int
|
||||||
|
drop int
|
||||||
|
ma msgArg
|
||||||
|
argBuf []byte
|
||||||
|
msgBuf []byte
|
||||||
|
scratch [MAX_CONTROL_LINE_SIZE]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
OP_START = iota
|
||||||
|
OP_PLUS
|
||||||
|
OP_PLUS_O
|
||||||
|
OP_PLUS_OK
|
||||||
|
OP_MINUS
|
||||||
|
OP_MINUS_E
|
||||||
|
OP_MINUS_ER
|
||||||
|
OP_MINUS_ERR
|
||||||
|
OP_MINUS_ERR_SPC
|
||||||
|
MINUS_ERR_ARG
|
||||||
|
OP_M
|
||||||
|
OP_MS
|
||||||
|
OP_MSG
|
||||||
|
OP_MSG_SPC
|
||||||
|
MSG_ARG
|
||||||
|
MSG_PAYLOAD
|
||||||
|
MSG_END
|
||||||
|
OP_P
|
||||||
|
OP_PI
|
||||||
|
OP_PIN
|
||||||
|
OP_PING
|
||||||
|
OP_PO
|
||||||
|
OP_PON
|
||||||
|
OP_PONG
|
||||||
|
OP_I
|
||||||
|
OP_IN
|
||||||
|
OP_INF
|
||||||
|
OP_INFO
|
||||||
|
OP_INFO_SPC
|
||||||
|
INFO_ARG
|
||||||
|
)
|
||||||
|
|
||||||
|
// parse is the fast protocol parser engine.
|
||||||
|
func (nc *Conn) parse(buf []byte) error {
|
||||||
|
var i int
|
||||||
|
var b byte
|
||||||
|
|
||||||
|
// Move to loop instead of range syntax to allow jumping of i
|
||||||
|
for i = 0; i < len(buf); i++ {
|
||||||
|
b = buf[i]
|
||||||
|
|
||||||
|
switch nc.ps.state {
|
||||||
|
case OP_START:
|
||||||
|
switch b {
|
||||||
|
case 'M', 'm':
|
||||||
|
nc.ps.state = OP_M
|
||||||
|
case 'P', 'p':
|
||||||
|
nc.ps.state = OP_P
|
||||||
|
case '+':
|
||||||
|
nc.ps.state = OP_PLUS
|
||||||
|
case '-':
|
||||||
|
nc.ps.state = OP_MINUS
|
||||||
|
case 'I', 'i':
|
||||||
|
nc.ps.state = OP_I
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_M:
|
||||||
|
switch b {
|
||||||
|
case 'S', 's':
|
||||||
|
nc.ps.state = OP_MS
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_MS:
|
||||||
|
switch b {
|
||||||
|
case 'G', 'g':
|
||||||
|
nc.ps.state = OP_MSG
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_MSG:
|
||||||
|
switch b {
|
||||||
|
case ' ', '\t':
|
||||||
|
nc.ps.state = OP_MSG_SPC
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_MSG_SPC:
|
||||||
|
switch b {
|
||||||
|
case ' ', '\t':
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
nc.ps.state = MSG_ARG
|
||||||
|
nc.ps.as = i
|
||||||
|
}
|
||||||
|
case MSG_ARG:
|
||||||
|
switch b {
|
||||||
|
case '\r':
|
||||||
|
nc.ps.drop = 1
|
||||||
|
case '\n':
|
||||||
|
var arg []byte
|
||||||
|
if nc.ps.argBuf != nil {
|
||||||
|
arg = nc.ps.argBuf
|
||||||
|
} else {
|
||||||
|
arg = buf[nc.ps.as : i-nc.ps.drop]
|
||||||
|
}
|
||||||
|
if err := nc.processMsgArgs(arg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, MSG_PAYLOAD
|
||||||
|
|
||||||
|
// jump ahead with the index. If this overruns
|
||||||
|
// what is left we fall out and process split
|
||||||
|
// buffer.
|
||||||
|
i = nc.ps.as + nc.ps.ma.size - 1
|
||||||
|
default:
|
||||||
|
if nc.ps.argBuf != nil {
|
||||||
|
nc.ps.argBuf = append(nc.ps.argBuf, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case MSG_PAYLOAD:
|
||||||
|
if nc.ps.msgBuf != nil {
|
||||||
|
if len(nc.ps.msgBuf) >= nc.ps.ma.size {
|
||||||
|
nc.processMsg(nc.ps.msgBuf)
|
||||||
|
nc.ps.argBuf, nc.ps.msgBuf, nc.ps.state = nil, nil, MSG_END
|
||||||
|
} else {
|
||||||
|
// copy as much as we can to the buffer and skip ahead.
|
||||||
|
toCopy := nc.ps.ma.size - len(nc.ps.msgBuf)
|
||||||
|
avail := len(buf) - i
|
||||||
|
|
||||||
|
if avail < toCopy {
|
||||||
|
toCopy = avail
|
||||||
|
}
|
||||||
|
|
||||||
|
if toCopy > 0 {
|
||||||
|
start := len(nc.ps.msgBuf)
|
||||||
|
// This is needed for copy to work.
|
||||||
|
nc.ps.msgBuf = nc.ps.msgBuf[:start+toCopy]
|
||||||
|
copy(nc.ps.msgBuf[start:], buf[i:i+toCopy])
|
||||||
|
// Update our index
|
||||||
|
i = (i + toCopy) - 1
|
||||||
|
} else {
|
||||||
|
nc.ps.msgBuf = append(nc.ps.msgBuf, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if i-nc.ps.as >= nc.ps.ma.size {
|
||||||
|
nc.processMsg(buf[nc.ps.as:i])
|
||||||
|
nc.ps.argBuf, nc.ps.msgBuf, nc.ps.state = nil, nil, MSG_END
|
||||||
|
}
|
||||||
|
case MSG_END:
|
||||||
|
switch b {
|
||||||
|
case '\n':
|
||||||
|
nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, OP_START
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case OP_PLUS:
|
||||||
|
switch b {
|
||||||
|
case 'O', 'o':
|
||||||
|
nc.ps.state = OP_PLUS_O
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_PLUS_O:
|
||||||
|
switch b {
|
||||||
|
case 'K', 'k':
|
||||||
|
nc.ps.state = OP_PLUS_OK
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_PLUS_OK:
|
||||||
|
switch b {
|
||||||
|
case '\n':
|
||||||
|
nc.processOK()
|
||||||
|
nc.ps.drop, nc.ps.state = 0, OP_START
|
||||||
|
}
|
||||||
|
case OP_MINUS:
|
||||||
|
switch b {
|
||||||
|
case 'E', 'e':
|
||||||
|
nc.ps.state = OP_MINUS_E
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_MINUS_E:
|
||||||
|
switch b {
|
||||||
|
case 'R', 'r':
|
||||||
|
nc.ps.state = OP_MINUS_ER
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_MINUS_ER:
|
||||||
|
switch b {
|
||||||
|
case 'R', 'r':
|
||||||
|
nc.ps.state = OP_MINUS_ERR
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_MINUS_ERR:
|
||||||
|
switch b {
|
||||||
|
case ' ', '\t':
|
||||||
|
nc.ps.state = OP_MINUS_ERR_SPC
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_MINUS_ERR_SPC:
|
||||||
|
switch b {
|
||||||
|
case ' ', '\t':
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
nc.ps.state = MINUS_ERR_ARG
|
||||||
|
nc.ps.as = i
|
||||||
|
}
|
||||||
|
case MINUS_ERR_ARG:
|
||||||
|
switch b {
|
||||||
|
case '\r':
|
||||||
|
nc.ps.drop = 1
|
||||||
|
case '\n':
|
||||||
|
var arg []byte
|
||||||
|
if nc.ps.argBuf != nil {
|
||||||
|
arg = nc.ps.argBuf
|
||||||
|
nc.ps.argBuf = nil
|
||||||
|
} else {
|
||||||
|
arg = buf[nc.ps.as : i-nc.ps.drop]
|
||||||
|
}
|
||||||
|
nc.processErr(string(arg))
|
||||||
|
nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, OP_START
|
||||||
|
default:
|
||||||
|
if nc.ps.argBuf != nil {
|
||||||
|
nc.ps.argBuf = append(nc.ps.argBuf, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case OP_P:
|
||||||
|
switch b {
|
||||||
|
case 'I', 'i':
|
||||||
|
nc.ps.state = OP_PI
|
||||||
|
case 'O', 'o':
|
||||||
|
nc.ps.state = OP_PO
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_PO:
|
||||||
|
switch b {
|
||||||
|
case 'N', 'n':
|
||||||
|
nc.ps.state = OP_PON
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_PON:
|
||||||
|
switch b {
|
||||||
|
case 'G', 'g':
|
||||||
|
nc.ps.state = OP_PONG
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_PONG:
|
||||||
|
switch b {
|
||||||
|
case '\n':
|
||||||
|
nc.processPong()
|
||||||
|
nc.ps.drop, nc.ps.state = 0, OP_START
|
||||||
|
}
|
||||||
|
case OP_PI:
|
||||||
|
switch b {
|
||||||
|
case 'N', 'n':
|
||||||
|
nc.ps.state = OP_PIN
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_PIN:
|
||||||
|
switch b {
|
||||||
|
case 'G', 'g':
|
||||||
|
nc.ps.state = OP_PING
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_PING:
|
||||||
|
switch b {
|
||||||
|
case '\n':
|
||||||
|
nc.processPing()
|
||||||
|
nc.ps.drop, nc.ps.state = 0, OP_START
|
||||||
|
}
|
||||||
|
case OP_I:
|
||||||
|
switch b {
|
||||||
|
case 'N', 'n':
|
||||||
|
nc.ps.state = OP_IN
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_IN:
|
||||||
|
switch b {
|
||||||
|
case 'F', 'f':
|
||||||
|
nc.ps.state = OP_INF
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_INF:
|
||||||
|
switch b {
|
||||||
|
case 'O', 'o':
|
||||||
|
nc.ps.state = OP_INFO
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_INFO:
|
||||||
|
switch b {
|
||||||
|
case ' ', '\t':
|
||||||
|
nc.ps.state = OP_INFO_SPC
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
case OP_INFO_SPC:
|
||||||
|
switch b {
|
||||||
|
case ' ', '\t':
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
nc.ps.state = INFO_ARG
|
||||||
|
nc.ps.as = i
|
||||||
|
}
|
||||||
|
case INFO_ARG:
|
||||||
|
switch b {
|
||||||
|
case '\r':
|
||||||
|
nc.ps.drop = 1
|
||||||
|
case '\n':
|
||||||
|
var arg []byte
|
||||||
|
if nc.ps.argBuf != nil {
|
||||||
|
arg = nc.ps.argBuf
|
||||||
|
nc.ps.argBuf = nil
|
||||||
|
} else {
|
||||||
|
arg = buf[nc.ps.as : i-nc.ps.drop]
|
||||||
|
}
|
||||||
|
nc.processAsyncInfo(arg)
|
||||||
|
nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, OP_START
|
||||||
|
default:
|
||||||
|
if nc.ps.argBuf != nil {
|
||||||
|
nc.ps.argBuf = append(nc.ps.argBuf, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
goto parseErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check for split buffer scenarios
|
||||||
|
if (nc.ps.state == MSG_ARG || nc.ps.state == MINUS_ERR_ARG || nc.ps.state == INFO_ARG) && nc.ps.argBuf == nil {
|
||||||
|
nc.ps.argBuf = nc.ps.scratch[:0]
|
||||||
|
nc.ps.argBuf = append(nc.ps.argBuf, buf[nc.ps.as:i-nc.ps.drop]...)
|
||||||
|
// FIXME, check max len
|
||||||
|
}
|
||||||
|
// Check for split msg
|
||||||
|
if nc.ps.state == MSG_PAYLOAD && nc.ps.msgBuf == nil {
|
||||||
|
// We need to clone the msgArg if it is still referencing the
|
||||||
|
// read buffer and we are not able to process the msg.
|
||||||
|
if nc.ps.argBuf == nil {
|
||||||
|
nc.cloneMsgArg()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we will overflow the scratch buffer, just create a
|
||||||
|
// new buffer to hold the split message.
|
||||||
|
if nc.ps.ma.size > cap(nc.ps.scratch)-len(nc.ps.argBuf) {
|
||||||
|
lrem := len(buf[nc.ps.as:])
|
||||||
|
|
||||||
|
nc.ps.msgBuf = make([]byte, lrem, nc.ps.ma.size)
|
||||||
|
copy(nc.ps.msgBuf, buf[nc.ps.as:])
|
||||||
|
} else {
|
||||||
|
nc.ps.msgBuf = nc.ps.scratch[len(nc.ps.argBuf):len(nc.ps.argBuf)]
|
||||||
|
nc.ps.msgBuf = append(nc.ps.msgBuf, (buf[nc.ps.as:])...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
parseErr:
|
||||||
|
return fmt.Errorf("nats: Parse Error [%d]: '%s'", nc.ps.state, buf[i:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// cloneMsgArg is used when the split buffer scenario has the pubArg in the existing read buffer, but
|
||||||
|
// we need to hold onto it into the next read.
|
||||||
|
func (nc *Conn) cloneMsgArg() {
|
||||||
|
nc.ps.argBuf = nc.ps.scratch[:0]
|
||||||
|
nc.ps.argBuf = append(nc.ps.argBuf, nc.ps.ma.subject...)
|
||||||
|
nc.ps.argBuf = append(nc.ps.argBuf, nc.ps.ma.reply...)
|
||||||
|
nc.ps.ma.subject = nc.ps.argBuf[:len(nc.ps.ma.subject)]
|
||||||
|
if nc.ps.ma.reply != nil {
|
||||||
|
nc.ps.ma.reply = nc.ps.argBuf[len(nc.ps.ma.subject):]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const argsLenMax = 4
|
||||||
|
|
||||||
|
func (nc *Conn) processMsgArgs(arg []byte) error {
|
||||||
|
// Unroll splitArgs to avoid runtime/heap issues
|
||||||
|
a := [argsLenMax][]byte{}
|
||||||
|
args := a[:0]
|
||||||
|
start := -1
|
||||||
|
for i, b := range arg {
|
||||||
|
switch b {
|
||||||
|
case ' ', '\t', '\r', '\n':
|
||||||
|
if start >= 0 {
|
||||||
|
args = append(args, arg[start:i])
|
||||||
|
start = -1
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if start < 0 {
|
||||||
|
start = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if start >= 0 {
|
||||||
|
args = append(args, arg[start:])
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 3:
|
||||||
|
nc.ps.ma.subject = args[0]
|
||||||
|
nc.ps.ma.sid = parseInt64(args[1])
|
||||||
|
nc.ps.ma.reply = nil
|
||||||
|
nc.ps.ma.size = int(parseInt64(args[2]))
|
||||||
|
case 4:
|
||||||
|
nc.ps.ma.subject = args[0]
|
||||||
|
nc.ps.ma.sid = parseInt64(args[1])
|
||||||
|
nc.ps.ma.reply = args[2]
|
||||||
|
nc.ps.ma.size = int(parseInt64(args[3]))
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("nats: processMsgArgs Parse Error: '%s'", arg)
|
||||||
|
}
|
||||||
|
if nc.ps.ma.sid < 0 {
|
||||||
|
return fmt.Errorf("nats: processMsgArgs Bad or Missing Sid: '%s'", arg)
|
||||||
|
}
|
||||||
|
if nc.ps.ma.size < 0 {
|
||||||
|
return fmt.Errorf("nats: processMsgArgs Bad or Missing Size: '%s'", arg)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ascii numbers 0-9
|
||||||
|
const (
|
||||||
|
ascii_0 = 48
|
||||||
|
ascii_9 = 57
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseInt64 expects decimal positive numbers. We
|
||||||
|
// return -1 to signal error
|
||||||
|
func parseInt64(d []byte) (n int64) {
|
||||||
|
if len(d) == 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
for _, dec := range d {
|
||||||
|
if dec < ascii_0 || dec > ascii_9 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
n = n*10 + (int64(dec) - ascii_0)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
21
vendor/github.com/nats-io/nuid/LICENSE
generated
vendored
Normal file
21
vendor/github.com/nats-io/nuid/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2012-2016 Apcera Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
66
vendor/github.com/nats-io/nuid/README.md
generated
vendored
Normal file
66
vendor/github.com/nats-io/nuid/README.md
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# NUID
|
||||||
|
|
||||||
|
[![License MIT](https://img.shields.io/npm/l/express.svg)](http://opensource.org/licenses/MIT)
|
||||||
|
[![ReportCard](http://goreportcard.com/badge/nats-io/nuid)](http://goreportcard.com/report/nats-io/nuid)
|
||||||
|
[![Build Status](https://travis-ci.org/nats-io/nuid.svg?branch=master)](http://travis-ci.org/nats-io/nuid)
|
||||||
|
[![Release](https://img.shields.io/badge/release-v1.0.0-1eb0fc.svg)](https://github.com/nats-io/nuid/releases/tag/v1.0.0)
|
||||||
|
[![GoDoc](http://godoc.org/github.com/nats-io/nuid?status.png)](http://godoc.org/github.com/nats-io/nuid)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/github/nats-io/nuid/badge.svg?branch=master)](https://coveralls.io/github/nats-io/nuid?branch=master)
|
||||||
|
|
||||||
|
A highly performant unique identifier generator.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Use the `go` command:
|
||||||
|
|
||||||
|
$ go get github.com/nats-io/nuid
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
```go
|
||||||
|
|
||||||
|
// Utilize the global locked instance
|
||||||
|
nuid := nuid.Next()
|
||||||
|
|
||||||
|
// Create an instance, these are not locked.
|
||||||
|
n := nuid.New()
|
||||||
|
nuid = n.Next()
|
||||||
|
|
||||||
|
// Generate a new crypto/rand seeded prefix.
|
||||||
|
// Generally not needed, happens automatically.
|
||||||
|
n.RandomizePrefix()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
NUID needs to be very fast to generate and be truly unique, all while being entropy pool friendly.
|
||||||
|
NUID uses 12 bytes of crypto generated data (entropy draining), and 10 bytes of pseudo-random
|
||||||
|
sequential data that increments with a pseudo-random increment.
|
||||||
|
|
||||||
|
Total length of a NUID string is 22 bytes of base 36 ascii text, so 36^22 or
|
||||||
|
17324272922341479351919144385642496 possibilities.
|
||||||
|
|
||||||
|
NUID can generate identifiers as fast as 60ns, or ~16 million per second. There is an associated
|
||||||
|
benchmark you can use to test performance on your own hardware.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
(The MIT License)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Apcera Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to
|
||||||
|
deal in the Software without restriction, including without limitation the
|
||||||
|
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
IN THE SOFTWARE.
|
124
vendor/github.com/nats-io/nuid/nuid.go
generated
vendored
Normal file
124
vendor/github.com/nats-io/nuid/nuid.go
generated
vendored
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// Copyright 2016 Apcera Inc. All rights reserved.
|
||||||
|
|
||||||
|
// A unique identifier generator that is high performance, very fast, and tries to be entropy pool friendly.
|
||||||
|
package nuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
prand "math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NUID needs to be very fast to generate and truly unique, all while being entropy pool friendly.
|
||||||
|
// We will use 12 bytes of crypto generated data (entropy draining), and 10 bytes of sequential data
|
||||||
|
// that is started at a pseudo random number and increments with a pseudo-random increment.
|
||||||
|
// Total is 22 bytes of base 62 ascii text :)
|
||||||
|
|
||||||
|
// Version of the library
|
||||||
|
const Version = "1.0.0"
|
||||||
|
|
||||||
|
const (
|
||||||
|
digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
|
base = 62
|
||||||
|
preLen = 12
|
||||||
|
seqLen = 10
|
||||||
|
maxSeq = int64(839299365868340224) // base^seqLen == 62^10
|
||||||
|
minInc = int64(33)
|
||||||
|
maxInc = int64(333)
|
||||||
|
totalLen = preLen + seqLen
|
||||||
|
)
|
||||||
|
|
||||||
|
type NUID struct {
|
||||||
|
pre []byte
|
||||||
|
seq int64
|
||||||
|
inc int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type lockedNUID struct {
|
||||||
|
sync.Mutex
|
||||||
|
*NUID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global NUID
|
||||||
|
var globalNUID *lockedNUID
|
||||||
|
|
||||||
|
// Seed sequential random with crypto or math/random and current time
|
||||||
|
// and generate crypto prefix.
|
||||||
|
func init() {
|
||||||
|
r, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
|
||||||
|
if err != nil {
|
||||||
|
prand.Seed(time.Now().UnixNano())
|
||||||
|
} else {
|
||||||
|
prand.Seed(r.Int64())
|
||||||
|
}
|
||||||
|
globalNUID = &lockedNUID{NUID: New()}
|
||||||
|
globalNUID.RandomizePrefix()
|
||||||
|
}
|
||||||
|
|
||||||
|
// New will generate a new NUID and properly initialize the prefix, sequential start, and sequential increment.
|
||||||
|
func New() *NUID {
|
||||||
|
n := &NUID{
|
||||||
|
seq: prand.Int63n(maxSeq),
|
||||||
|
inc: minInc + prand.Int63n(maxInc-minInc),
|
||||||
|
pre: make([]byte, preLen),
|
||||||
|
}
|
||||||
|
n.RandomizePrefix()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the next NUID string from the global locked NUID instance.
|
||||||
|
func Next() string {
|
||||||
|
globalNUID.Lock()
|
||||||
|
nuid := globalNUID.Next()
|
||||||
|
globalNUID.Unlock()
|
||||||
|
return nuid
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the next NUID string.
|
||||||
|
func (n *NUID) Next() string {
|
||||||
|
// Increment and capture.
|
||||||
|
n.seq += n.inc
|
||||||
|
if n.seq >= maxSeq {
|
||||||
|
n.RandomizePrefix()
|
||||||
|
n.resetSequential()
|
||||||
|
}
|
||||||
|
seq := n.seq
|
||||||
|
|
||||||
|
// Copy prefix
|
||||||
|
var b [totalLen]byte
|
||||||
|
bs := b[:preLen]
|
||||||
|
copy(bs, n.pre)
|
||||||
|
|
||||||
|
// copy in the seq in base36.
|
||||||
|
for i, l := len(b), seq; i > preLen; l /= base {
|
||||||
|
i -= 1
|
||||||
|
b[i] = digits[l%base]
|
||||||
|
}
|
||||||
|
return string(b[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resets the sequential portion of the NUID.
|
||||||
|
func (n *NUID) resetSequential() {
|
||||||
|
n.seq = prand.Int63n(maxSeq)
|
||||||
|
n.inc = minInc + prand.Int63n(maxInc-minInc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a new prefix from crypto/rand.
|
||||||
|
// This call *can* drain entropy and will be called automatically when we exhaust the sequential range.
|
||||||
|
// Will panic if it gets an error from rand.Int()
|
||||||
|
func (n *NUID) RandomizePrefix() {
|
||||||
|
var cb [preLen]byte
|
||||||
|
cbs := cb[:]
|
||||||
|
if nb, err := rand.Read(cbs); nb != preLen || err != nil {
|
||||||
|
panic(fmt.Sprintf("nuid: failed generating crypto random number: %v\n", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < preLen; i++ {
|
||||||
|
n.pre[i] = digits[int(cbs[i])%base]
|
||||||
|
}
|
||||||
|
}
|
18
vendor/vendor.json
vendored
18
vendor/vendor.json
vendored
@ -142,6 +142,24 @@
|
|||||||
"revision": "6f50cd1d784b2bea46167b6929f16c0d12eefbfb",
|
"revision": "6f50cd1d784b2bea46167b6929f16c0d12eefbfb",
|
||||||
"revisionTime": "2016-08-16T22:25:11Z"
|
"revisionTime": "2016-08-16T22:25:11Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "Nj7vQ2GlvJiPP7sqJX5AurrDSD4=",
|
||||||
|
"path": "github.com/nats-io/nats",
|
||||||
|
"revision": "70b70be17b77e8da86b6a3bcfe94fb22718a8dd0",
|
||||||
|
"revisionTime": "2016-09-16T18:17:35Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "qAIuHdtQrDi2fFmpaP1dZLbgpGc=",
|
||||||
|
"path": "github.com/nats-io/nats/encoders/builtin",
|
||||||
|
"revision": "70b70be17b77e8da86b6a3bcfe94fb22718a8dd0",
|
||||||
|
"revisionTime": "2016-09-16T18:17:35Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "i8Yom1KrpDKwjlGH/gpJGAQmo68=",
|
||||||
|
"path": "github.com/nats-io/nuid",
|
||||||
|
"revision": "289cccf02c178dc782430d534e3c1f5b72af807f",
|
||||||
|
"revisionTime": "2016-09-27T04:49:45Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "github.com/pkg/profile",
|
"path": "github.com/pkg/profile",
|
||||||
"revision": "c78aac22bd43883fd2817833b982153dcac17b3b",
|
"revision": "c78aac22bd43883fd2817833b982153dcac17b3b",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user