@@ -38,20 +38,37 @@ type option interface {
38
38
39
39
// IsAuthChange indicates if this option requires reloading authorization.
40
40
IsAuthChange () bool
41
+
42
+ // IsClusterPermsChange indicates if this option requires reloading
43
+ // cluster permissions.
44
+ IsClusterPermsChange () bool
45
+ }
46
+
47
+ // noopOption is a base struct that provides default no-op behaviors.
48
+ type noopOption struct {}
49
+
50
+ func (n noopOption ) IsLoggingChange () bool {
51
+ return false
52
+ }
53
+
54
+ func (n noopOption ) IsAuthChange () bool {
55
+ return false
56
+ }
57
+
58
+ func (n noopOption ) IsClusterPermsChange () bool {
59
+ return false
41
60
}
42
61
43
62
// loggingOption is a base struct that provides default option behaviors for
44
63
// logging-related options.
45
- type loggingOption struct {}
64
+ type loggingOption struct {
65
+ noopOption
66
+ }
46
67
47
68
func (l loggingOption ) IsLoggingChange () bool {
48
69
return true
49
70
}
50
71
51
- func (l loggingOption ) IsAuthChange () bool {
52
- return false
53
- }
54
-
55
72
// traceOption implements the option interface for the `trace` setting.
56
73
type traceOption struct {
57
74
loggingOption
@@ -119,17 +136,6 @@ func (r *remoteSyslogOption) Apply(server *Server) {
119
136
server .Noticef ("Reloaded: remote_syslog = %v" , r .newValue )
120
137
}
121
138
122
- // noopOption is a base struct that provides default no-op behaviors.
123
- type noopOption struct {}
124
-
125
- func (n noopOption ) IsLoggingChange () bool {
126
- return false
127
- }
128
-
129
- func (n noopOption ) IsAuthChange () bool {
130
- return false
131
- }
132
-
133
139
// tlsOption implements the option interface for the `tls` setting.
134
140
type tlsOption struct {
135
141
noopOption
@@ -164,10 +170,8 @@ func (t *tlsTimeoutOption) Apply(server *Server) {
164
170
}
165
171
166
172
// authOption is a base struct that provides default option behaviors.
167
- type authOption struct {}
168
-
169
- func (o authOption ) IsLoggingChange () bool {
170
- return false
173
+ type authOption struct {
174
+ noopOption
171
175
}
172
176
173
177
func (o authOption ) IsAuthChange () bool {
@@ -235,7 +239,8 @@ func (u *usersOption) Apply(server *Server) {
235
239
// clusterOption implements the option interface for the `cluster` setting.
236
240
type clusterOption struct {
237
241
authOption
238
- newValue ClusterOpts
242
+ newValue ClusterOpts
243
+ permsChanged bool
239
244
}
240
245
241
246
// Apply the cluster change.
@@ -256,6 +261,10 @@ func (c *clusterOption) Apply(server *Server) {
256
261
server .Noticef ("Reloaded: cluster" )
257
262
}
258
263
264
+ func (c * clusterOption ) IsClusterPermsChange () bool {
265
+ return c .permsChanged
266
+ }
267
+
259
268
// routesOption implements the option interface for the cluster `routes`
260
269
// setting.
261
270
type routesOption struct {
@@ -503,6 +512,10 @@ func (s *Server) reloadOptions(newOpts *Options) error {
503
512
if err != nil {
504
513
return err
505
514
}
515
+ // Need to save off previous cluster permissions
516
+ s .mu .Lock ()
517
+ s .oldClusterPerms = s .opts .Cluster .Permissions
518
+ s .mu .Unlock ()
506
519
s .setOpts (newOpts )
507
520
s .applyOptions (changed )
508
521
return nil
@@ -557,10 +570,12 @@ func (s *Server) diffOptions(newOpts *Options) ([]option, error) {
557
570
diffOpts = append (diffOpts , & usersOption {newValue : newValue .([]* User )})
558
571
case "cluster" :
559
572
newClusterOpts := newValue .(ClusterOpts )
560
- if err := validateClusterOpts (oldValue .(ClusterOpts ), newClusterOpts ); err != nil {
573
+ oldClusterOpts := oldValue .(ClusterOpts )
574
+ if err := validateClusterOpts (oldClusterOpts , newClusterOpts ); err != nil {
561
575
return nil , err
562
576
}
563
- diffOpts = append (diffOpts , & clusterOption {newValue : newClusterOpts })
577
+ permsChanged := ! reflect .DeepEqual (newClusterOpts .Permissions , oldClusterOpts .Permissions )
578
+ diffOpts = append (diffOpts , & clusterOption {newValue : newClusterOpts , permsChanged : permsChanged })
564
579
case "routes" :
565
580
add , remove := diffRoutes (oldValue .([]* url.URL ), newValue .([]* url.URL ))
566
581
diffOpts = append (diffOpts , & routesOption {add : add , remove : remove })
@@ -612,8 +627,9 @@ func (s *Server) diffOptions(newOpts *Options) ([]option, error) {
612
627
613
628
func (s * Server ) applyOptions (opts []option ) {
614
629
var (
615
- reloadLogging = false
616
- reloadAuth = false
630
+ reloadLogging = false
631
+ reloadAuth = false
632
+ reloadClusterPerms = false
617
633
)
618
634
for _ , opt := range opts {
619
635
opt .Apply (s )
@@ -623,6 +639,9 @@ func (s *Server) applyOptions(opts []option) {
623
639
if opt .IsAuthChange () {
624
640
reloadAuth = true
625
641
}
642
+ if opt .IsClusterPermsChange () {
643
+ reloadClusterPerms = true
644
+ }
626
645
}
627
646
628
647
if reloadLogging {
@@ -631,6 +650,9 @@ func (s *Server) applyOptions(opts []option) {
631
650
if reloadAuth {
632
651
s .reloadAuthorization ()
633
652
}
653
+ if reloadClusterPerms {
654
+ s .reloadClusterPermissions ()
655
+ }
634
656
635
657
s .Noticef ("Reloaded server configuration" )
636
658
}
@@ -674,6 +696,123 @@ func (s *Server) reloadAuthorization() {
674
696
}
675
697
}
676
698
699
+ // reloadClusterPermissions reconfigures the cluster's permssions
700
+ // and set the permissions to all existing routes, sending an
701
+ // update INFO protocol so that remote can resend their local
702
+ // subs if needed, and sending local subs matching cluster's
703
+ // import subjects.
704
+ func (s * Server ) reloadClusterPermissions () {
705
+ s .mu .Lock ()
706
+ var (
707
+ infoJSON []byte
708
+ oldPerms = s .oldClusterPerms
709
+ newPerms = s .opts .Cluster .Permissions
710
+ routes = make (map [uint64 ]* client , len (s .routes ))
711
+ withNewProto int
712
+ )
713
+ // We can clear this now that we have captured it with oldPerms.
714
+ s .oldClusterPerms = nil
715
+ // Get all connected routes
716
+ for i , route := range s .routes {
717
+ // Count the number of routes that can understand receiving INFO updates.
718
+ route .mu .Lock ()
719
+ if route .opts .Protocol >= routeProtoInfo {
720
+ withNewProto ++
721
+ }
722
+ route .mu .Unlock ()
723
+ routes [i ] = route
724
+ }
725
+ // If new permissions is nil, then clear routeInfo import/export
726
+ if newPerms == nil {
727
+ s .routeInfo .Import = nil
728
+ s .routeInfo .Export = nil
729
+ } else {
730
+ s .routeInfo .Import = newPerms .Import
731
+ s .routeInfo .Export = newPerms .Export
732
+ }
733
+ // Regenerate route INFO
734
+ s .generateRouteInfoJSON ()
735
+ infoJSON = s .routeInfoJSON
736
+ s .mu .Unlock ()
737
+
738
+ // If there were no route, we are done
739
+ if len (routes ) == 0 {
740
+ return
741
+ }
742
+
743
+ // If only older servers, simply close all routes and they will do the right
744
+ // thing on reconnect.
745
+ if withNewProto == 0 {
746
+ for _ , route := range routes {
747
+ route .closeConnection (RouteRemoved )
748
+ }
749
+ return
750
+ }
751
+
752
+ // Fake clients to test cluster permissions
753
+ oldPermsTester := & client {}
754
+ oldPermsTester .setRoutePermissions (oldPerms )
755
+ newPermsTester := & client {}
756
+ newPermsTester .setRoutePermissions (newPerms )
757
+
758
+ var (
759
+ _localSubs [4096 ]* subscription
760
+ localSubs = _localSubs [:0 ]
761
+ subsNeedSUB []* subscription
762
+ subsNeedUNSUB []* subscription
763
+ deleteRoutedSubs []* subscription
764
+ )
765
+ s .sl .localSubs (& localSubs )
766
+
767
+ // Go through all local subscriptions
768
+ for _ , sub := range localSubs {
769
+ // Get all subs that can now be imported
770
+ couldImportThen := oldPermsTester .canImport (sub .subject )
771
+ canImportNow := newPermsTester .canImport (sub .subject )
772
+ if canImportNow {
773
+ // If we could not before, then will need to send a SUB protocol.
774
+ if ! couldImportThen {
775
+ subsNeedSUB = append (subsNeedSUB , sub )
776
+ }
777
+ } else if couldImportThen {
778
+ // We were previously able to import this sub, but now
779
+ // we can't so we need to send an UNSUB protocol
780
+ subsNeedUNSUB = append (subsNeedUNSUB , sub )
781
+ }
782
+ }
783
+
784
+ for _ , route := range routes {
785
+ route .mu .Lock ()
786
+ // If route is to older server, simply close connection.
787
+ if route .opts .Protocol < routeProtoInfo {
788
+ route .mu .Unlock ()
789
+ route .closeConnection (RouteRemoved )
790
+ continue
791
+ }
792
+ route .setRoutePermissions (newPerms )
793
+ for _ , sub := range route .subs {
794
+ // If we can't export, we need to drop the subscriptions that
795
+ // we have on behalf of this route.
796
+ if ! route .canExport (sub .subject ) {
797
+ delete (route .subs , string (sub .sid ))
798
+ deleteRoutedSubs = append (deleteRoutedSubs , sub )
799
+ }
800
+ }
801
+ // Send an update INFO, which will allow remote server to show
802
+ // our current route config in monitoring and resend subscriptions
803
+ // that we now possibly allow with a change of Export permissions.
804
+ route .sendInfo (infoJSON )
805
+ // Now send SUB and UNSUB protocols as needed.
806
+ closed := route .sendRouteSubProtos (subsNeedSUB , nil )
807
+ if ! closed {
808
+ route .sendRouteUnSubProtos (subsNeedUNSUB , nil )
809
+ }
810
+ route .mu .Unlock ()
811
+ }
812
+ // Remove as a batch all the subs that we have removed from each route.
813
+ s .sl .RemoveBatch (deleteRoutedSubs )
814
+ }
815
+
677
816
// validateClusterOpts ensures the new ClusterOpts does not change host or
678
817
// port, which do not support reload.
679
818
func validateClusterOpts (old , new ClusterOpts ) error {
0 commit comments