mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
First pass account configuration for jetstream
Signed-off-by: Derek Collison <derek@nats.io>
This commit is contained in:
@@ -1132,7 +1132,7 @@ func lexSkip(lx *lexer, nextState stateFn) stateFn {
|
||||
|
||||
// Tests to see if we have a number suffix
|
||||
func isNumberSuffix(r rune) bool {
|
||||
return r == 'k' || r == 'K' || r == 'm' || r == 'M' || r == 'g' || r == 'G'
|
||||
return r == 'k' || r == 'K' || r == 'm' || r == 'M' || r == 'g' || r == 'G' || r == 't' || r == 'T' || r == 'p' || r == 'P' || r == 'e' || r == 'E'
|
||||
}
|
||||
|
||||
// Tests for both key separators
|
||||
|
||||
@@ -263,6 +263,18 @@ func (p *parser) processItem(it item, fp string) error {
|
||||
setValue(it, num*1000*1000*1000)
|
||||
case "gb":
|
||||
setValue(it, num*1024*1024*1024)
|
||||
case "t":
|
||||
setValue(it, num*1000*1000*1000*1000)
|
||||
case "tb":
|
||||
setValue(it, num*1024*1024*1024*1024)
|
||||
case "p":
|
||||
setValue(it, num*1000*1000*1000*1000*1000)
|
||||
case "pb":
|
||||
setValue(it, num*1024*1024*1024*1024*1024)
|
||||
case "e":
|
||||
setValue(it, num*1000*1000*1000*1000*1000*1000)
|
||||
case "eb":
|
||||
setValue(it, num*1024*1024*1024*1024*1024*1024)
|
||||
}
|
||||
case itemFloat:
|
||||
num, err := strconv.ParseFloat(it.val, 64)
|
||||
|
||||
@@ -45,7 +45,7 @@ type Account struct {
|
||||
sqmu sync.Mutex
|
||||
sl *Sublist
|
||||
ic *client
|
||||
isid int
|
||||
isid uint64
|
||||
etmr *time.Timer
|
||||
ctmr *time.Timer
|
||||
strack map[string]sconns
|
||||
@@ -63,6 +63,7 @@ type Account struct {
|
||||
imports importMap
|
||||
exports exportMap
|
||||
js *jsAccount
|
||||
jsLimits *JetStreamAccountLimits
|
||||
limits
|
||||
nae int32
|
||||
pruning bool
|
||||
@@ -242,6 +243,8 @@ func (a *Account) shallowCopy() *Account {
|
||||
}
|
||||
}
|
||||
}
|
||||
na.jsLimits = a.jsLimits
|
||||
|
||||
return na
|
||||
}
|
||||
|
||||
@@ -1066,7 +1069,7 @@ func (a *Account) addServiceImportSub(si *serviceImport) error {
|
||||
a.ic.acc = a
|
||||
}
|
||||
c := a.ic
|
||||
sid := strconv.Itoa(a.isid + 1)
|
||||
sid := strconv.FormatUint(a.isid+1, 10)
|
||||
a.mu.RUnlock()
|
||||
|
||||
// This will happen in parsing when the account has not been properly setup.
|
||||
|
||||
@@ -166,11 +166,37 @@ func (s *Server) EnableJetStream(config *JetStreamConfig) error {
|
||||
if err := s.GlobalAccount().EnableJetStream(nil); err != nil {
|
||||
return fmt.Errorf("Error enabling jetstream on the global account")
|
||||
}
|
||||
} else if err := s.enableAllJetStreamAccounts(); err != nil {
|
||||
return fmt.Errorf("Error enabling jetstream on configured accounts: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// enableAllJetStreamAccounts walk all configured accounts and turn on jetstream if requested.
|
||||
func (s *Server) enableAllJetStreamAccounts() error {
|
||||
var jsAccounts []*Account
|
||||
|
||||
s.mu.Lock()
|
||||
s.accounts.Range(func(k, v interface{}) bool {
|
||||
acc := v.(*Account)
|
||||
if acc.jsLimits != nil {
|
||||
jsAccounts = append(jsAccounts, acc)
|
||||
}
|
||||
return true
|
||||
})
|
||||
s.mu.Unlock()
|
||||
|
||||
// Process any jetstream enabled accounts here.
|
||||
for _, acc := range jsAccounts {
|
||||
if err := acc.EnableJetStream(acc.jsLimits); err != nil {
|
||||
return err
|
||||
}
|
||||
acc.jsLimits = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// JetStreamEnabled reports if jetstream is enabled.
|
||||
func (s *Server) JetStreamEnabled() bool {
|
||||
s.mu.Lock()
|
||||
|
||||
138
server/opts.go
138
server/opts.go
@@ -1133,38 +1133,124 @@ func parseGateway(v interface{}, o *Options, errors *[]error, warnings *[]error)
|
||||
return nil
|
||||
}
|
||||
|
||||
var dynamicJSAccountLimits = &JetStreamAccountLimits{-1, -1, -1, -1}
|
||||
|
||||
// Parses jetstream account limits for an account. Simple setup with boolen is allowed, and we will
|
||||
// use dynamic account limits.
|
||||
func parseJetStreamForAccount(v interface{}, acc *Account, errors *[]error, warnings *[]error) error {
|
||||
var lt token
|
||||
|
||||
tk, v := unwrapValue(v, <)
|
||||
|
||||
// Value here can be bool, or string "enabled" or a map.
|
||||
switch vv := v.(type) {
|
||||
case bool:
|
||||
if vv {
|
||||
acc.jsLimits = dynamicJSAccountLimits
|
||||
}
|
||||
case string:
|
||||
switch strings.ToLower(vv) {
|
||||
case "enabled", "enable":
|
||||
acc.jsLimits = dynamicJSAccountLimits
|
||||
case "disabled", "disable":
|
||||
acc.jsLimits = nil
|
||||
default:
|
||||
return &configErr{tk, fmt.Sprintf("Expected 'enabled' or 'disabled' for string value, got '%s'", vv)}
|
||||
}
|
||||
case map[string]interface{}:
|
||||
jsLimits := &JetStreamAccountLimits{-1, -1, -1, -1}
|
||||
for mk, mv := range vv {
|
||||
tk, mv = unwrapValue(mv, <)
|
||||
switch strings.ToLower(mk) {
|
||||
case "max_memory", "max_mem", "mem", "memory":
|
||||
vv, ok := mv.(int64)
|
||||
if !ok {
|
||||
return &configErr{tk, fmt.Sprintf("Expected a parseable size for %q, got %v", mk, mv)}
|
||||
}
|
||||
jsLimits.MaxMemory = int64(vv)
|
||||
case "max_store", "max_file", "max_disk", "store", "disk":
|
||||
vv, ok := mv.(int64)
|
||||
if !ok {
|
||||
return &configErr{tk, fmt.Sprintf("Expected a parseable size for %q, got %v", mk, mv)}
|
||||
}
|
||||
jsLimits.MaxStore = int64(vv)
|
||||
case "max_streams", "streams":
|
||||
vv, ok := mv.(int64)
|
||||
if !ok {
|
||||
return &configErr{tk, fmt.Sprintf("Expected a parseable size for %q, got %v", mk, mv)}
|
||||
}
|
||||
jsLimits.MaxStreams = int(vv)
|
||||
case "max_consumers", "consumers":
|
||||
vv, ok := mv.(int64)
|
||||
if !ok {
|
||||
return &configErr{tk, fmt.Sprintf("Expected a parseable size for %q, got %v", mk, mv)}
|
||||
}
|
||||
jsLimits.MaxConsumers = int(vv)
|
||||
default:
|
||||
if !tk.IsUsedVariable() {
|
||||
err := &unknownConfigFieldErr{
|
||||
field: mk,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
},
|
||||
}
|
||||
*errors = append(*errors, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
acc.jsLimits = jsLimits
|
||||
default:
|
||||
return &configErr{tk, fmt.Sprintf("Expected map, bool or string to define JetStream, got %T", v)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse enablement of jetstream for a server.
|
||||
func parseJetStream(v interface{}, opts *Options, errors *[]error, warnings *[]error) error {
|
||||
var lt token
|
||||
|
||||
tk, v := unwrapValue(v, <)
|
||||
cm, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
return &configErr{tk, fmt.Sprintf("Expected map to define JetStream, got %T", v)}
|
||||
}
|
||||
|
||||
opts.JetStream = true
|
||||
|
||||
for mk, mv := range cm {
|
||||
tk, mv = unwrapValue(mv, <)
|
||||
switch strings.ToLower(mk) {
|
||||
case "store_dir", "storedir":
|
||||
opts.StoreDir = mv.(string)
|
||||
case "max_memory_store", "max_mem_store":
|
||||
opts.JetStreamMaxMemory = mv.(int64)
|
||||
case "max_file_store":
|
||||
opts.JetStreamMaxStore = mv.(int64)
|
||||
// Value here can be bool, or string "enabled" or a map.
|
||||
switch vv := v.(type) {
|
||||
case bool:
|
||||
opts.JetStream = v.(bool)
|
||||
case string:
|
||||
switch strings.ToLower(vv) {
|
||||
case "enabled", "enable":
|
||||
opts.JetStream = true
|
||||
case "disabled", "disable":
|
||||
opts.JetStream = false
|
||||
default:
|
||||
if !tk.IsUsedVariable() {
|
||||
err := &unknownConfigFieldErr{
|
||||
field: mk,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
},
|
||||
return &configErr{tk, fmt.Sprintf("Expected 'enabled' or 'disabled' for string value, got '%s'", vv)}
|
||||
}
|
||||
case map[string]interface{}:
|
||||
for mk, mv := range vv {
|
||||
tk, mv = unwrapValue(mv, <)
|
||||
switch strings.ToLower(mk) {
|
||||
case "store_dir", "storedir":
|
||||
opts.StoreDir = mv.(string)
|
||||
case "max_memory_store", "max_mem_store":
|
||||
opts.JetStreamMaxMemory = mv.(int64)
|
||||
case "max_file_store":
|
||||
opts.JetStreamMaxStore = mv.(int64)
|
||||
default:
|
||||
if !tk.IsUsedVariable() {
|
||||
err := &unknownConfigFieldErr{
|
||||
field: mk,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
},
|
||||
}
|
||||
*errors = append(*errors, err)
|
||||
continue
|
||||
}
|
||||
*errors = append(*errors, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
default:
|
||||
return &configErr{tk, fmt.Sprintf("Expected map, bool or string to define JetStream, got %T", v)}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -1687,6 +1773,12 @@ func parseAccounts(v interface{}, opts *Options, errors *[]error, warnings *[]er
|
||||
}
|
||||
exportStreams = append(exportStreams, streams...)
|
||||
exportServices = append(exportServices, services...)
|
||||
case "jetstream":
|
||||
err := parseJetStreamForAccount(mv, acc, errors, warnings)
|
||||
if err != nil {
|
||||
*errors = append(*errors, err)
|
||||
continue
|
||||
}
|
||||
case "users":
|
||||
nkeys, users, err := parseUsers(mv, opts, errors, warnings)
|
||||
if err != nil {
|
||||
|
||||
@@ -480,7 +480,6 @@ func (s *Server) configureAccounts() error {
|
||||
ea.approved[sub] = acc
|
||||
}
|
||||
}
|
||||
|
||||
s.accounts.Range(func(k, v interface{}) bool {
|
||||
acc := v.(*Account)
|
||||
// Exports
|
||||
|
||||
@@ -4922,6 +4922,85 @@ func TestJetStreamSingleInstanceRemoteAccess(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func clientConnectToServerWithUP(t *testing.T, opts *server.Options, user, pass string) *nats.Conn {
|
||||
curl := fmt.Sprintf("nats://%s:%s@%s:%d", user, pass, opts.Host, opts.Port)
|
||||
nc, err := nats.Connect(curl, nats.Name("JS-UP-TEST"), nats.ReconnectWait(5*time.Millisecond), nats.MaxReconnects(-1))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
return nc
|
||||
}
|
||||
|
||||
func TestJetStreamMultipleAccountsBasics(t *testing.T) {
|
||||
conf := createConfFile(t, []byte(`
|
||||
listen: 127.0.0.1:-1
|
||||
jetstream: enabled
|
||||
accounts: {
|
||||
A: {
|
||||
jetstream: enabled
|
||||
users: [ {user: ua, password: pwd} ]
|
||||
},
|
||||
B: {
|
||||
jetstream: {max_mem: 1GB, max_store: 1TB, max_streams: 10, max_consumers: 1k}
|
||||
users: [ {user: ub, password: pwd} ]
|
||||
},
|
||||
C: {
|
||||
users: [ {user: uc, password: pwd} ]
|
||||
},
|
||||
}
|
||||
`))
|
||||
defer os.Remove(conf)
|
||||
|
||||
s, opts := RunServerWithConfig(conf)
|
||||
defer s.Shutdown()
|
||||
|
||||
if !s.JetStreamEnabled() {
|
||||
t.Fatalf("Expected JetStream to be enabled")
|
||||
}
|
||||
|
||||
nc := clientConnectToServerWithUP(t, opts, "ua", "pwd")
|
||||
defer nc.Close()
|
||||
|
||||
resp, _ := nc.Request(server.JetStreamEnabled, nil, 250*time.Millisecond)
|
||||
expectOKResponse(t, resp)
|
||||
|
||||
nc = clientConnectToServerWithUP(t, opts, "ub", "pwd")
|
||||
defer nc.Close()
|
||||
|
||||
resp, _ = nc.Request(server.JetStreamEnabled, nil, 250*time.Millisecond)
|
||||
expectOKResponse(t, resp)
|
||||
|
||||
resp, err := nc.Request(server.JetStreamInfo, nil, time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
var info server.JetStreamAccountStats
|
||||
if err := json.Unmarshal(resp.Data, &info); err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
limits := info.Limits
|
||||
if limits.MaxStreams != 10 {
|
||||
t.Fatalf("Expected 10 for MaxStreams, got %d", limits.MaxStreams)
|
||||
}
|
||||
if limits.MaxConsumers != 1000 {
|
||||
t.Fatalf("Expected MaxConsumers of %d, got %d", 1000, limits.MaxConsumers)
|
||||
}
|
||||
gb := int64(1024 * 1024 * 1024)
|
||||
if limits.MaxMemory != gb {
|
||||
t.Fatalf("Expected MaxMemory to be 1GB, got %d", limits.MaxMemory)
|
||||
}
|
||||
if limits.MaxStore != 1024*gb {
|
||||
t.Fatalf("Expected MaxStore to be 1TB, got %d", limits.MaxStore)
|
||||
}
|
||||
// Check C is not enabled.
|
||||
nc = clientConnectToServerWithUP(t, opts, "uc", "pwd")
|
||||
defer nc.Close()
|
||||
|
||||
if _, err = nc.Request(server.JetStreamEnabled, nil, 250*time.Millisecond); err == nil {
|
||||
t.Fatalf("Expected no response for account c")
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// Benchmark placeholders
|
||||
////////////////////////////////////////
|
||||
|
||||
Reference in New Issue
Block a user