Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
| ... | ... |
@@ -140,7 +140,7 @@ clone git github.com/docker/docker-credential-helpers v0.3.0 |
| 140 | 140 |
clone git github.com/docker/containerd 0ac3cd1be170d180b2baed755e8f0da547ceb267 |
| 141 | 141 |
|
| 142 | 142 |
# cluster |
| 143 |
-clone git github.com/docker/swarmkit e1c0d64515d839b76e2ef33d396c74933753ffaf |
|
| 143 |
+clone git github.com/docker/swarmkit cb6d81316727941665594f153434e5ce2e425c9b |
|
| 144 | 144 |
clone git github.com/golang/mock bd3c8e81be01eef76d4b503f5e687d2d1354d2d9 |
| 145 | 145 |
clone git github.com/gogo/protobuf 43a2e0b1c32252bfbbdf81f7faa7a88fb3fa4028 |
| 146 | 146 |
clone git github.com/cloudflare/cfssl b895b0549c0ff676f92cf09ba971ae02bb41367b |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"math/rand" |
| 6 | 6 |
"reflect" |
| 7 |
+ "sync" |
|
| 7 | 8 |
"time" |
| 8 | 9 |
|
| 9 | 10 |
"github.com/docker/swarmkit/api" |
| ... | ... |
@@ -31,11 +32,13 @@ type Agent struct {
|
| 31 | 31 |
sessionq chan sessionOperation |
| 32 | 32 |
worker Worker |
| 33 | 33 |
|
| 34 |
- started chan struct{}
|
|
| 35 |
- ready chan struct{}
|
|
| 36 |
- stopped chan struct{} // requests shutdown
|
|
| 37 |
- closed chan struct{} // only closed in run
|
|
| 38 |
- err error // read only after closed is closed |
|
| 34 |
+ started chan struct{}
|
|
| 35 |
+ startOnce sync.Once // start only once |
|
| 36 |
+ ready chan struct{}
|
|
| 37 |
+ stopped chan struct{} // requests shutdown
|
|
| 38 |
+ stopOnce sync.Once // only allow stop to be called once |
|
| 39 |
+ closed chan struct{} // only closed in run
|
|
| 40 |
+ err error // read only after closed is closed |
|
| 39 | 41 |
} |
| 40 | 42 |
|
| 41 | 43 |
// New returns a new agent, ready for task dispatch. |
| ... | ... |
@@ -59,57 +62,50 @@ func New(config *Config) (*Agent, error) {
|
| 59 | 59 |
|
| 60 | 60 |
// Start begins execution of the agent in the provided context, if not already |
| 61 | 61 |
// started. |
| 62 |
+// |
|
| 63 |
+// Start returns an error if the agent has already started. |
|
| 62 | 64 |
func (a *Agent) Start(ctx context.Context) error {
|
| 63 |
- select {
|
|
| 64 |
- case <-a.started: |
|
| 65 |
- select {
|
|
| 66 |
- case <-a.closed: |
|
| 67 |
- return a.err |
|
| 68 |
- case <-a.stopped: |
|
| 69 |
- return errAgentStopped |
|
| 70 |
- case <-ctx.Done(): |
|
| 71 |
- return ctx.Err() |
|
| 72 |
- default: |
|
| 73 |
- return errAgentStarted |
|
| 74 |
- } |
|
| 75 |
- case <-ctx.Done(): |
|
| 76 |
- return ctx.Err() |
|
| 77 |
- default: |
|
| 78 |
- } |
|
| 65 |
+ err := errAgentStarted |
|
| 79 | 66 |
|
| 80 |
- close(a.started) |
|
| 81 |
- go a.run(ctx) |
|
| 67 |
+ a.startOnce.Do(func() {
|
|
| 68 |
+ close(a.started) |
|
| 69 |
+ go a.run(ctx) |
|
| 70 |
+ err = nil // clear error above, only once. |
|
| 71 |
+ }) |
|
| 82 | 72 |
|
| 83 |
- return nil |
|
| 73 |
+ return err |
|
| 84 | 74 |
} |
| 85 | 75 |
|
| 86 | 76 |
// Stop shuts down the agent, blocking until full shutdown. If the agent is not |
| 87 |
-// started, Stop will block until Started. |
|
| 77 |
+// started, Stop will block until the agent has fully shutdown. |
|
| 88 | 78 |
func (a *Agent) Stop(ctx context.Context) error {
|
| 89 | 79 |
select {
|
| 90 | 80 |
case <-a.started: |
| 91 |
- select {
|
|
| 92 |
- case <-a.closed: |
|
| 93 |
- return a.err |
|
| 94 |
- case <-a.stopped: |
|
| 95 |
- select {
|
|
| 96 |
- case <-a.closed: |
|
| 97 |
- return a.err |
|
| 98 |
- case <-ctx.Done(): |
|
| 99 |
- return ctx.Err() |
|
| 100 |
- } |
|
| 101 |
- case <-ctx.Done(): |
|
| 102 |
- return ctx.Err() |
|
| 103 |
- default: |
|
| 104 |
- close(a.stopped) |
|
| 105 |
- // recurse and wait for closure |
|
| 106 |
- return a.Stop(ctx) |
|
| 107 |
- } |
|
| 108 |
- case <-ctx.Done(): |
|
| 109 |
- return ctx.Err() |
|
| 110 | 81 |
default: |
| 111 | 82 |
return errAgentNotStarted |
| 112 | 83 |
} |
| 84 |
+ |
|
| 85 |
+ a.stop() |
|
| 86 |
+ |
|
| 87 |
+ // wait till closed or context cancelled |
|
| 88 |
+ select {
|
|
| 89 |
+ case <-a.closed: |
|
| 90 |
+ return nil |
|
| 91 |
+ case <-ctx.Done(): |
|
| 92 |
+ return ctx.Err() |
|
| 93 |
+ } |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+// stop signals the agent shutdown process, returning true if this call was the |
|
| 97 |
+// first to actually shutdown the agent. |
|
| 98 |
+func (a *Agent) stop() bool {
|
|
| 99 |
+ var stopped bool |
|
| 100 |
+ a.stopOnce.Do(func() {
|
|
| 101 |
+ close(a.stopped) |
|
| 102 |
+ stopped = true |
|
| 103 |
+ }) |
|
| 104 |
+ |
|
| 105 |
+ return stopped |
|
| 113 | 106 |
} |
| 114 | 107 |
|
| 115 | 108 |
// Err returns the error that caused the agent to shutdown or nil. Err blocks |
| ... | ... |
@@ -133,7 +129,7 @@ func (a *Agent) run(ctx context.Context) {
|
| 133 | 133 |
defer cancel() |
| 134 | 134 |
defer close(a.closed) // full shutdown. |
| 135 | 135 |
|
| 136 |
- ctx = log.WithLogger(ctx, log.G(ctx).WithField("module", "agent"))
|
|
| 136 |
+ ctx = log.WithModule(ctx, "agent") |
|
| 137 | 137 |
|
| 138 | 138 |
log.G(ctx).Debugf("(*Agent).run")
|
| 139 | 139 |
defer log.G(ctx).Debugf("(*Agent).run exited")
|
| ... | ... |
@@ -197,11 +193,6 @@ func (a *Agent) run(ctx context.Context) {
|
| 197 | 197 |
sessionq = nil |
| 198 | 198 |
// if we're here before <-registered, do nothing for that event |
| 199 | 199 |
registered = nil |
| 200 |
- |
|
| 201 |
- // Bounce the connection. |
|
| 202 |
- if a.config.Picker != nil {
|
|
| 203 |
- a.config.Picker.Reset() |
|
| 204 |
- } |
|
| 205 | 200 |
case <-session.closed: |
| 206 | 201 |
log.G(ctx).Debugf("agent: rebuild session")
|
| 207 | 202 |
|
| ... | ... |
@@ -218,6 +209,7 @@ func (a *Agent) run(ctx context.Context) {
|
| 218 | 218 |
if a.err == nil {
|
| 219 | 219 |
a.err = ctx.Err() |
| 220 | 220 |
} |
| 221 |
+ session.close() |
|
| 221 | 222 |
|
| 222 | 223 |
return |
| 223 | 224 |
} |
| ... | ... |
@@ -7,7 +7,7 @@ import ( |
| 7 | 7 |
"github.com/docker/swarmkit/agent/exec" |
| 8 | 8 |
"github.com/docker/swarmkit/api" |
| 9 | 9 |
"github.com/docker/swarmkit/picker" |
| 10 |
- "google.golang.org/grpc" |
|
| 10 |
+ "google.golang.org/grpc/credentials" |
|
| 11 | 11 |
) |
| 12 | 12 |
|
| 13 | 13 |
// Config provides values for an Agent. |
| ... | ... |
@@ -19,15 +19,6 @@ type Config struct {
|
| 19 | 19 |
// updated with managers weights as observed by the agent. |
| 20 | 20 |
Managers picker.Remotes |
| 21 | 21 |
|
| 22 |
- // Conn specifies the client connection Agent will use. |
|
| 23 |
- Conn *grpc.ClientConn |
|
| 24 |
- |
|
| 25 |
- // Picker is the picker used by Conn. |
|
| 26 |
- // TODO(aaronl): This is only part of the config to allow resetting the |
|
| 27 |
- // GRPC connection. This should be refactored to address the coupling |
|
| 28 |
- // between Conn and Picker. |
|
| 29 |
- Picker *picker.Picker |
|
| 30 |
- |
|
| 31 | 22 |
// Executor specifies the executor to use for the agent. |
| 32 | 23 |
Executor exec.Executor |
| 33 | 24 |
|
| ... | ... |
@@ -36,11 +27,14 @@ type Config struct {
|
| 36 | 36 |
|
| 37 | 37 |
// NotifyRoleChange channel receives new roles from session messages. |
| 38 | 38 |
NotifyRoleChange chan<- api.NodeRole |
| 39 |
+ |
|
| 40 |
+ // Credentials is credentials for grpc connection to manager. |
|
| 41 |
+ Credentials credentials.TransportAuthenticator |
|
| 39 | 42 |
} |
| 40 | 43 |
|
| 41 | 44 |
func (c *Config) validate() error {
|
| 42 |
- if c.Conn == nil {
|
|
| 43 |
- return fmt.Errorf("agent: Connection is required")
|
|
| 45 |
+ if c.Credentials == nil {
|
|
| 46 |
+ return fmt.Errorf("agent: Credentials is required")
|
|
| 44 | 47 |
} |
| 45 | 48 |
|
| 46 | 49 |
if c.Executor == nil {
|
| ... | ... |
@@ -10,10 +10,11 @@ var ( |
| 10 | 10 |
ErrClosed = errors.New("agent: closed")
|
| 11 | 11 |
|
| 12 | 12 |
errNodeNotRegistered = fmt.Errorf("node not registered")
|
| 13 |
+ errNodeStarted = errors.New("node: already started")
|
|
| 14 |
+ errNodeNotStarted = errors.New("node: not started")
|
|
| 13 | 15 |
|
| 14 |
- errAgentNotStarted = errors.New("agent: not started")
|
|
| 15 | 16 |
errAgentStarted = errors.New("agent: already started")
|
| 16 |
- errAgentStopped = errors.New("agent: stopped")
|
|
| 17 |
+ errAgentNotStarted = errors.New("agent: not started")
|
|
| 17 | 18 |
|
| 18 | 19 |
errTaskNoContoller = errors.New("agent: no task controller")
|
| 19 | 20 |
errTaskNotAssigned = errors.New("agent: task not assigned")
|
| ... | ... |
@@ -2,11 +2,11 @@ package exec |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 |
- "reflect" |
|
| 6 | 5 |
"time" |
| 7 | 6 |
|
| 8 | 7 |
"github.com/Sirupsen/logrus" |
| 9 | 8 |
"github.com/docker/swarmkit/api" |
| 9 |
+ "github.com/docker/swarmkit/api/equality" |
|
| 10 | 10 |
"github.com/docker/swarmkit/log" |
| 11 | 11 |
"github.com/docker/swarmkit/protobuf/ptypes" |
| 12 | 12 |
"github.com/pkg/errors" |
| ... | ... |
@@ -186,7 +186,7 @@ func Do(ctx context.Context, task *api.Task, ctlr Controller) (*api.TaskStatus, |
| 186 | 186 |
defer func() {
|
| 187 | 187 |
logStateChange(ctx, task.DesiredState, task.Status.State, status.State) |
| 188 | 188 |
|
| 189 |
- if !reflect.DeepEqual(status, task.Status) {
|
|
| 189 |
+ if !equality.TaskStatusesEqualStable(status, &task.Status) {
|
|
| 190 | 190 |
status.Timestamp = ptypes.MustTimestampProto(time.Now()) |
| 191 | 191 |
} |
| 192 | 192 |
}() |
| ... | ... |
@@ -3,7 +3,7 @@ package agent |
| 3 | 3 |
import "golang.org/x/net/context" |
| 4 | 4 |
|
| 5 | 5 |
// runctx blocks until the function exits, closed is closed, or the context is |
| 6 |
-// cancelled. Call as part os go statement. |
|
| 6 |
+// cancelled. Call as part of go statement. |
|
| 7 | 7 |
func runctx(ctx context.Context, closed chan struct{}, errs chan error, fn func(ctx context.Context) error) {
|
| 8 | 8 |
select {
|
| 9 | 9 |
case errs <- fn(ctx): |
| ... | ... |
@@ -89,7 +89,9 @@ type Node struct {
|
| 89 | 89 |
nodeID string |
| 90 | 90 |
nodeMembership api.NodeSpec_Membership |
| 91 | 91 |
started chan struct{}
|
| 92 |
+ startOnce sync.Once |
|
| 92 | 93 |
stopped chan struct{}
|
| 94 |
+ stopOnce sync.Once |
|
| 93 | 95 |
ready chan struct{} // closed when agent has completed registration and manager(if enabled) is ready to receive control requests
|
| 94 | 96 |
certificateRequested chan struct{} // closed when certificate issue request has been sent by node
|
| 95 | 97 |
closed chan struct{}
|
| ... | ... |
@@ -137,26 +139,15 @@ func NewNode(c *NodeConfig) (*Node, error) {
|
| 137 | 137 |
|
| 138 | 138 |
// Start starts a node instance. |
| 139 | 139 |
func (n *Node) Start(ctx context.Context) error {
|
| 140 |
- select {
|
|
| 141 |
- case <-n.started: |
|
| 142 |
- select {
|
|
| 143 |
- case <-n.closed: |
|
| 144 |
- return n.err |
|
| 145 |
- case <-n.stopped: |
|
| 146 |
- return errAgentStopped |
|
| 147 |
- case <-ctx.Done(): |
|
| 148 |
- return ctx.Err() |
|
| 149 |
- default: |
|
| 150 |
- return errAgentStarted |
|
| 151 |
- } |
|
| 152 |
- case <-ctx.Done(): |
|
| 153 |
- return ctx.Err() |
|
| 154 |
- default: |
|
| 155 |
- } |
|
| 140 |
+ err := errNodeStarted |
|
| 156 | 141 |
|
| 157 |
- close(n.started) |
|
| 158 |
- go n.run(ctx) |
|
| 159 |
- return nil |
|
| 142 |
+ n.startOnce.Do(func() {
|
|
| 143 |
+ close(n.started) |
|
| 144 |
+ go n.run(ctx) |
|
| 145 |
+ err = nil // clear error above, only once. |
|
| 146 |
+ }) |
|
| 147 |
+ |
|
| 148 |
+ return err |
|
| 160 | 149 |
} |
| 161 | 150 |
|
| 162 | 151 |
func (n *Node) run(ctx context.Context) (err error) {
|
| ... | ... |
@@ -166,7 +157,7 @@ func (n *Node) run(ctx context.Context) (err error) {
|
| 166 | 166 |
}() |
| 167 | 167 |
ctx, cancel := context.WithCancel(ctx) |
| 168 | 168 |
defer cancel() |
| 169 |
- ctx = log.WithLogger(ctx, log.G(ctx).WithField("module", "node"))
|
|
| 169 |
+ ctx = log.WithModule(ctx, "node") |
|
| 170 | 170 |
|
| 171 | 171 |
go func() {
|
| 172 | 172 |
select {
|
| ... | ... |
@@ -325,27 +316,19 @@ func (n *Node) run(ctx context.Context) (err error) {
|
| 325 | 325 |
func (n *Node) Stop(ctx context.Context) error {
|
| 326 | 326 |
select {
|
| 327 | 327 |
case <-n.started: |
| 328 |
- select {
|
|
| 329 |
- case <-n.closed: |
|
| 330 |
- return n.err |
|
| 331 |
- case <-n.stopped: |
|
| 332 |
- select {
|
|
| 333 |
- case <-n.closed: |
|
| 334 |
- return n.err |
|
| 335 |
- case <-ctx.Done(): |
|
| 336 |
- return ctx.Err() |
|
| 337 |
- } |
|
| 338 |
- case <-ctx.Done(): |
|
| 339 |
- return ctx.Err() |
|
| 340 |
- default: |
|
| 341 |
- close(n.stopped) |
|
| 342 |
- // recurse and wait for closure |
|
| 343 |
- return n.Stop(ctx) |
|
| 344 |
- } |
|
| 328 |
+ default: |
|
| 329 |
+ return errNodeNotStarted |
|
| 330 |
+ } |
|
| 331 |
+ |
|
| 332 |
+ n.stopOnce.Do(func() {
|
|
| 333 |
+ close(n.stopped) |
|
| 334 |
+ }) |
|
| 335 |
+ |
|
| 336 |
+ select {
|
|
| 337 |
+ case <-n.closed: |
|
| 338 |
+ return nil |
|
| 345 | 339 |
case <-ctx.Done(): |
| 346 | 340 |
return ctx.Err() |
| 347 |
- default: |
|
| 348 |
- return errAgentNotStarted |
|
| 349 | 341 |
} |
| 350 | 342 |
} |
| 351 | 343 |
|
| ... | ... |
@@ -361,31 +344,21 @@ func (n *Node) Err(ctx context.Context) error {
|
| 361 | 361 |
} |
| 362 | 362 |
|
| 363 | 363 |
func (n *Node) runAgent(ctx context.Context, db *bolt.DB, creds credentials.TransportAuthenticator, ready chan<- struct{}) error {
|
| 364 |
- var manager api.Peer |
|
| 365 | 364 |
select {
|
| 366 | 365 |
case <-ctx.Done(): |
| 367 |
- case manager = <-n.remotes.WaitSelect(ctx): |
|
| 366 |
+ case <-n.remotes.WaitSelect(ctx): |
|
| 368 | 367 |
} |
| 369 | 368 |
if ctx.Err() != nil {
|
| 370 | 369 |
return ctx.Err() |
| 371 | 370 |
} |
| 372 |
- picker := picker.NewPicker(n.remotes, manager.Addr) |
|
| 373 |
- conn, err := grpc.Dial(manager.Addr, |
|
| 374 |
- grpc.WithPicker(picker), |
|
| 375 |
- grpc.WithTransportCredentials(creds), |
|
| 376 |
- grpc.WithBackoffMaxDelay(maxSessionFailureBackoff)) |
|
| 377 |
- if err != nil {
|
|
| 378 |
- return err |
|
| 379 |
- } |
|
| 380 | 371 |
|
| 381 | 372 |
agent, err := New(&Config{
|
| 382 | 373 |
Hostname: n.config.Hostname, |
| 383 | 374 |
Managers: n.remotes, |
| 384 | 375 |
Executor: n.config.Executor, |
| 385 | 376 |
DB: db, |
| 386 |
- Conn: conn, |
|
| 387 |
- Picker: picker, |
|
| 388 | 377 |
NotifyRoleChange: n.roleChangeReq, |
| 378 |
+ Credentials: creds, |
|
| 389 | 379 |
}) |
| 390 | 380 |
if err != nil {
|
| 391 | 381 |
return err |
| ... | ... |
@@ -6,6 +6,7 @@ import ( |
| 6 | 6 |
|
| 7 | 7 |
"github.com/docker/swarmkit/api" |
| 8 | 8 |
"github.com/docker/swarmkit/log" |
| 9 |
+ "github.com/docker/swarmkit/picker" |
|
| 9 | 10 |
"github.com/docker/swarmkit/protobuf/ptypes" |
| 10 | 11 |
"golang.org/x/net/context" |
| 11 | 12 |
"google.golang.org/grpc" |
| ... | ... |
@@ -27,6 +28,9 @@ var ( |
| 27 | 27 |
// flow into the agent, such as task assignment, are called back into the |
| 28 | 28 |
// agent through errs, messages and tasks. |
| 29 | 29 |
type session struct {
|
| 30 |
+ conn *grpc.ClientConn |
|
| 31 |
+ addr string |
|
| 32 |
+ |
|
| 30 | 33 |
agent *Agent |
| 31 | 34 |
sessionID string |
| 32 | 35 |
session api.Dispatcher_SessionClient |
| ... | ... |
@@ -41,12 +45,27 @@ type session struct {
|
| 41 | 41 |
func newSession(ctx context.Context, agent *Agent, delay time.Duration) *session {
|
| 42 | 42 |
s := &session{
|
| 43 | 43 |
agent: agent, |
| 44 |
- errs: make(chan error), |
|
| 44 |
+ errs: make(chan error, 1), |
|
| 45 | 45 |
messages: make(chan *api.SessionMessage), |
| 46 | 46 |
tasks: make(chan *api.TasksMessage), |
| 47 | 47 |
registered: make(chan struct{}),
|
| 48 | 48 |
closed: make(chan struct{}),
|
| 49 | 49 |
} |
| 50 |
+ peer, err := agent.config.Managers.Select() |
|
| 51 |
+ if err != nil {
|
|
| 52 |
+ s.errs <- err |
|
| 53 |
+ return s |
|
| 54 |
+ } |
|
| 55 |
+ cc, err := grpc.Dial(peer.Addr, |
|
| 56 |
+ grpc.WithTransportCredentials(agent.config.Credentials), |
|
| 57 |
+ grpc.WithTimeout(dispatcherRPCTimeout), |
|
| 58 |
+ ) |
|
| 59 |
+ if err != nil {
|
|
| 60 |
+ s.errs <- err |
|
| 61 |
+ return s |
|
| 62 |
+ } |
|
| 63 |
+ s.addr = peer.Addr |
|
| 64 |
+ s.conn = cc |
|
| 50 | 65 |
|
| 51 | 66 |
go s.run(ctx, delay) |
| 52 | 67 |
return s |
| ... | ... |
@@ -77,8 +96,6 @@ func (s *session) run(ctx context.Context, delay time.Duration) {
|
| 77 | 77 |
func (s *session) start(ctx context.Context) error {
|
| 78 | 78 |
log.G(ctx).Debugf("(*session).start")
|
| 79 | 79 |
|
| 80 |
- client := api.NewDispatcherClient(s.agent.config.Conn) |
|
| 81 |
- |
|
| 82 | 80 |
description, err := s.agent.config.Executor.Describe(ctx) |
| 83 | 81 |
if err != nil {
|
| 84 | 82 |
log.G(ctx).WithError(err).WithField("executor", s.agent.config.Executor).
|
| ... | ... |
@@ -103,6 +120,8 @@ func (s *session) start(ctx context.Context) error {
|
| 103 | 103 |
// Need to run Session in a goroutine since there's no way to set a |
| 104 | 104 |
// timeout for an individual Recv call in a stream. |
| 105 | 105 |
go func() {
|
| 106 |
+ client := api.NewDispatcherClient(s.conn) |
|
| 107 |
+ |
|
| 106 | 108 |
stream, err = client.Session(sessionCtx, &api.SessionRequest{
|
| 107 | 109 |
Description: description, |
| 108 | 110 |
}) |
| ... | ... |
@@ -133,7 +152,7 @@ func (s *session) start(ctx context.Context) error {
|
| 133 | 133 |
|
| 134 | 134 |
func (s *session) heartbeat(ctx context.Context) error {
|
| 135 | 135 |
log.G(ctx).Debugf("(*session).heartbeat")
|
| 136 |
- client := api.NewDispatcherClient(s.agent.config.Conn) |
|
| 136 |
+ client := api.NewDispatcherClient(s.conn) |
|
| 137 | 137 |
heartbeat := time.NewTimer(1) // send out a heartbeat right away |
| 138 | 138 |
defer heartbeat.Stop() |
| 139 | 139 |
|
| ... | ... |
@@ -195,7 +214,7 @@ func (s *session) handleSessionMessage(ctx context.Context, msg *api.SessionMess |
| 195 | 195 |
|
| 196 | 196 |
func (s *session) watch(ctx context.Context) error {
|
| 197 | 197 |
log.G(ctx).Debugf("(*session).watch")
|
| 198 |
- client := api.NewDispatcherClient(s.agent.config.Conn) |
|
| 198 |
+ client := api.NewDispatcherClient(s.conn) |
|
| 199 | 199 |
watch, err := client.Tasks(ctx, &api.TasksRequest{
|
| 200 | 200 |
SessionID: s.sessionID}) |
| 201 | 201 |
if err != nil {
|
| ... | ... |
@@ -221,7 +240,7 @@ func (s *session) watch(ctx context.Context) error {
|
| 221 | 221 |
// sendTaskStatus uses the current session to send the status of a single task. |
| 222 | 222 |
func (s *session) sendTaskStatus(ctx context.Context, taskID string, status *api.TaskStatus) error {
|
| 223 | 223 |
|
| 224 |
- client := api.NewDispatcherClient(s.agent.config.Conn) |
|
| 224 |
+ client := api.NewDispatcherClient(s.conn) |
|
| 225 | 225 |
if _, err := client.UpdateTaskStatus(ctx, &api.UpdateTaskStatusRequest{
|
| 226 | 226 |
SessionID: s.sessionID, |
| 227 | 227 |
Updates: []*api.UpdateTaskStatusRequest_TaskStatusUpdate{
|
| ... | ... |
@@ -262,7 +281,7 @@ func (s *session) sendTaskStatuses(ctx context.Context, updates ...*api.UpdateTa |
| 262 | 262 |
return updates, ctx.Err() |
| 263 | 263 |
} |
| 264 | 264 |
|
| 265 |
- client := api.NewDispatcherClient(s.agent.config.Conn) |
|
| 265 |
+ client := api.NewDispatcherClient(s.conn) |
|
| 266 | 266 |
n := batchSize |
| 267 | 267 |
|
| 268 | 268 |
if len(updates) < n {
|
| ... | ... |
@@ -285,6 +304,10 @@ func (s *session) close() error {
|
| 285 | 285 |
case <-s.closed: |
| 286 | 286 |
return errSessionClosed |
| 287 | 287 |
default: |
| 288 |
+ if s.conn != nil {
|
|
| 289 |
+ s.agent.config.Managers.ObserveIfExists(api.Peer{Addr: s.addr}, -picker.DefaultObservationWeight)
|
|
| 290 |
+ s.conn.Close() |
|
| 291 |
+ } |
|
| 288 | 292 |
close(s.closed) |
| 289 | 293 |
return nil |
| 290 | 294 |
} |
| ... | ... |
@@ -68,7 +68,7 @@ func (tm *taskManager) run(ctx context.Context) {
|
| 68 | 68 |
ctx, cancelAll := context.WithCancel(ctx) |
| 69 | 69 |
defer cancelAll() // cancel all child operations on exit. |
| 70 | 70 |
|
| 71 |
- ctx = log.WithLogger(ctx, log.G(ctx).WithField("module", "taskmanager"))
|
|
| 71 |
+ ctx = log.WithModule(ctx, "taskmanager") |
|
| 72 | 72 |
|
| 73 | 73 |
var ( |
| 74 | 74 |
opctx context.Context |
| ... | ... |
@@ -57,7 +57,7 @@ func (w *worker) Init(ctx context.Context) error {
|
| 57 | 57 |
w.mu.Lock() |
| 58 | 58 |
defer w.mu.Unlock() |
| 59 | 59 |
|
| 60 |
- ctx = log.WithLogger(ctx, log.G(ctx).WithField("module", "worker"))
|
|
| 60 |
+ ctx = log.WithModule(ctx, "worker") |
|
| 61 | 61 |
|
| 62 | 62 |
// TODO(stevvooe): Start task cleanup process. |
| 63 | 63 |
|
| ... | ... |
@@ -668,12 +668,12 @@ func encodeVarintCa(data []byte, offset int, v uint64) int {
|
| 668 | 668 |
|
| 669 | 669 |
type raftProxyCAServer struct {
|
| 670 | 670 |
local CAServer |
| 671 |
- connSelector *raftpicker.ConnSelector |
|
| 671 |
+ connSelector raftpicker.Interface |
|
| 672 | 672 |
cluster raftpicker.RaftCluster |
| 673 | 673 |
ctxMods []func(context.Context) (context.Context, error) |
| 674 | 674 |
} |
| 675 | 675 |
|
| 676 |
-func NewRaftProxyCAServer(local CAServer, connSelector *raftpicker.ConnSelector, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) CAServer {
|
|
| 676 |
+func NewRaftProxyCAServer(local CAServer, connSelector raftpicker.Interface, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) CAServer {
|
|
| 677 | 677 |
redirectChecker := func(ctx context.Context) (context.Context, error) {
|
| 678 | 678 |
s, ok := transport.StreamFromContext(ctx) |
| 679 | 679 |
if !ok {
|
| ... | ... |
@@ -724,17 +724,30 @@ func (p *raftProxyCAServer) GetRootCACertificate(ctx context.Context, r *GetRoot |
| 724 | 724 |
if err != nil {
|
| 725 | 725 |
return nil, err |
| 726 | 726 |
} |
| 727 |
+ |
|
| 728 |
+ defer func() {
|
|
| 729 |
+ if err != nil {
|
|
| 730 |
+ errStr := err.Error() |
|
| 731 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 732 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 733 |
+ strings.Contains(errStr, "connection error") || |
|
| 734 |
+ grpc.Code(err) == codes.Internal {
|
|
| 735 |
+ p.connSelector.Reset() |
|
| 736 |
+ } |
|
| 737 |
+ } |
|
| 738 |
+ }() |
|
| 739 |
+ |
|
| 727 | 740 |
return NewCAClient(conn).GetRootCACertificate(ctx, r) |
| 728 | 741 |
} |
| 729 | 742 |
|
| 730 | 743 |
type raftProxyNodeCAServer struct {
|
| 731 | 744 |
local NodeCAServer |
| 732 |
- connSelector *raftpicker.ConnSelector |
|
| 745 |
+ connSelector raftpicker.Interface |
|
| 733 | 746 |
cluster raftpicker.RaftCluster |
| 734 | 747 |
ctxMods []func(context.Context) (context.Context, error) |
| 735 | 748 |
} |
| 736 | 749 |
|
| 737 |
-func NewRaftProxyNodeCAServer(local NodeCAServer, connSelector *raftpicker.ConnSelector, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) NodeCAServer {
|
|
| 750 |
+func NewRaftProxyNodeCAServer(local NodeCAServer, connSelector raftpicker.Interface, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) NodeCAServer {
|
|
| 738 | 751 |
redirectChecker := func(ctx context.Context) (context.Context, error) {
|
| 739 | 752 |
s, ok := transport.StreamFromContext(ctx) |
| 740 | 753 |
if !ok {
|
| ... | ... |
@@ -785,6 +798,19 @@ func (p *raftProxyNodeCAServer) IssueNodeCertificate(ctx context.Context, r *Iss |
| 785 | 785 |
if err != nil {
|
| 786 | 786 |
return nil, err |
| 787 | 787 |
} |
| 788 |
+ |
|
| 789 |
+ defer func() {
|
|
| 790 |
+ if err != nil {
|
|
| 791 |
+ errStr := err.Error() |
|
| 792 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 793 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 794 |
+ strings.Contains(errStr, "connection error") || |
|
| 795 |
+ grpc.Code(err) == codes.Internal {
|
|
| 796 |
+ p.connSelector.Reset() |
|
| 797 |
+ } |
|
| 798 |
+ } |
|
| 799 |
+ }() |
|
| 800 |
+ |
|
| 788 | 801 |
return NewNodeCAClient(conn).IssueNodeCertificate(ctx, r) |
| 789 | 802 |
} |
| 790 | 803 |
|
| ... | ... |
@@ -801,6 +827,19 @@ func (p *raftProxyNodeCAServer) NodeCertificateStatus(ctx context.Context, r *No |
| 801 | 801 |
if err != nil {
|
| 802 | 802 |
return nil, err |
| 803 | 803 |
} |
| 804 |
+ |
|
| 805 |
+ defer func() {
|
|
| 806 |
+ if err != nil {
|
|
| 807 |
+ errStr := err.Error() |
|
| 808 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 809 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 810 |
+ strings.Contains(errStr, "connection error") || |
|
| 811 |
+ grpc.Code(err) == codes.Internal {
|
|
| 812 |
+ p.connSelector.Reset() |
|
| 813 |
+ } |
|
| 814 |
+ } |
|
| 815 |
+ }() |
|
| 816 |
+ |
|
| 804 | 817 |
return NewNodeCAClient(conn).NodeCertificateStatus(ctx, r) |
| 805 | 818 |
} |
| 806 | 819 |
|
| ... | ... |
@@ -4239,12 +4239,12 @@ func encodeVarintControl(data []byte, offset int, v uint64) int {
|
| 4239 | 4239 |
|
| 4240 | 4240 |
type raftProxyControlServer struct {
|
| 4241 | 4241 |
local ControlServer |
| 4242 |
- connSelector *raftpicker.ConnSelector |
|
| 4242 |
+ connSelector raftpicker.Interface |
|
| 4243 | 4243 |
cluster raftpicker.RaftCluster |
| 4244 | 4244 |
ctxMods []func(context.Context) (context.Context, error) |
| 4245 | 4245 |
} |
| 4246 | 4246 |
|
| 4247 |
-func NewRaftProxyControlServer(local ControlServer, connSelector *raftpicker.ConnSelector, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) ControlServer {
|
|
| 4247 |
+func NewRaftProxyControlServer(local ControlServer, connSelector raftpicker.Interface, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) ControlServer {
|
|
| 4248 | 4248 |
redirectChecker := func(ctx context.Context) (context.Context, error) {
|
| 4249 | 4249 |
s, ok := transport.StreamFromContext(ctx) |
| 4250 | 4250 |
if !ok {
|
| ... | ... |
@@ -4295,6 +4295,19 @@ func (p *raftProxyControlServer) GetNode(ctx context.Context, r *GetNodeRequest) |
| 4295 | 4295 |
if err != nil {
|
| 4296 | 4296 |
return nil, err |
| 4297 | 4297 |
} |
| 4298 |
+ |
|
| 4299 |
+ defer func() {
|
|
| 4300 |
+ if err != nil {
|
|
| 4301 |
+ errStr := err.Error() |
|
| 4302 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4303 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4304 |
+ strings.Contains(errStr, "connection error") || |
|
| 4305 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4306 |
+ p.connSelector.Reset() |
|
| 4307 |
+ } |
|
| 4308 |
+ } |
|
| 4309 |
+ }() |
|
| 4310 |
+ |
|
| 4298 | 4311 |
return NewControlClient(conn).GetNode(ctx, r) |
| 4299 | 4312 |
} |
| 4300 | 4313 |
|
| ... | ... |
@@ -4311,6 +4324,19 @@ func (p *raftProxyControlServer) ListNodes(ctx context.Context, r *ListNodesRequ |
| 4311 | 4311 |
if err != nil {
|
| 4312 | 4312 |
return nil, err |
| 4313 | 4313 |
} |
| 4314 |
+ |
|
| 4315 |
+ defer func() {
|
|
| 4316 |
+ if err != nil {
|
|
| 4317 |
+ errStr := err.Error() |
|
| 4318 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4319 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4320 |
+ strings.Contains(errStr, "connection error") || |
|
| 4321 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4322 |
+ p.connSelector.Reset() |
|
| 4323 |
+ } |
|
| 4324 |
+ } |
|
| 4325 |
+ }() |
|
| 4326 |
+ |
|
| 4314 | 4327 |
return NewControlClient(conn).ListNodes(ctx, r) |
| 4315 | 4328 |
} |
| 4316 | 4329 |
|
| ... | ... |
@@ -4327,6 +4353,19 @@ func (p *raftProxyControlServer) UpdateNode(ctx context.Context, r *UpdateNodeRe |
| 4327 | 4327 |
if err != nil {
|
| 4328 | 4328 |
return nil, err |
| 4329 | 4329 |
} |
| 4330 |
+ |
|
| 4331 |
+ defer func() {
|
|
| 4332 |
+ if err != nil {
|
|
| 4333 |
+ errStr := err.Error() |
|
| 4334 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4335 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4336 |
+ strings.Contains(errStr, "connection error") || |
|
| 4337 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4338 |
+ p.connSelector.Reset() |
|
| 4339 |
+ } |
|
| 4340 |
+ } |
|
| 4341 |
+ }() |
|
| 4342 |
+ |
|
| 4330 | 4343 |
return NewControlClient(conn).UpdateNode(ctx, r) |
| 4331 | 4344 |
} |
| 4332 | 4345 |
|
| ... | ... |
@@ -4343,6 +4382,19 @@ func (p *raftProxyControlServer) RemoveNode(ctx context.Context, r *RemoveNodeRe |
| 4343 | 4343 |
if err != nil {
|
| 4344 | 4344 |
return nil, err |
| 4345 | 4345 |
} |
| 4346 |
+ |
|
| 4347 |
+ defer func() {
|
|
| 4348 |
+ if err != nil {
|
|
| 4349 |
+ errStr := err.Error() |
|
| 4350 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4351 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4352 |
+ strings.Contains(errStr, "connection error") || |
|
| 4353 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4354 |
+ p.connSelector.Reset() |
|
| 4355 |
+ } |
|
| 4356 |
+ } |
|
| 4357 |
+ }() |
|
| 4358 |
+ |
|
| 4346 | 4359 |
return NewControlClient(conn).RemoveNode(ctx, r) |
| 4347 | 4360 |
} |
| 4348 | 4361 |
|
| ... | ... |
@@ -4359,6 +4411,19 @@ func (p *raftProxyControlServer) GetTask(ctx context.Context, r *GetTaskRequest) |
| 4359 | 4359 |
if err != nil {
|
| 4360 | 4360 |
return nil, err |
| 4361 | 4361 |
} |
| 4362 |
+ |
|
| 4363 |
+ defer func() {
|
|
| 4364 |
+ if err != nil {
|
|
| 4365 |
+ errStr := err.Error() |
|
| 4366 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4367 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4368 |
+ strings.Contains(errStr, "connection error") || |
|
| 4369 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4370 |
+ p.connSelector.Reset() |
|
| 4371 |
+ } |
|
| 4372 |
+ } |
|
| 4373 |
+ }() |
|
| 4374 |
+ |
|
| 4362 | 4375 |
return NewControlClient(conn).GetTask(ctx, r) |
| 4363 | 4376 |
} |
| 4364 | 4377 |
|
| ... | ... |
@@ -4375,6 +4440,19 @@ func (p *raftProxyControlServer) ListTasks(ctx context.Context, r *ListTasksRequ |
| 4375 | 4375 |
if err != nil {
|
| 4376 | 4376 |
return nil, err |
| 4377 | 4377 |
} |
| 4378 |
+ |
|
| 4379 |
+ defer func() {
|
|
| 4380 |
+ if err != nil {
|
|
| 4381 |
+ errStr := err.Error() |
|
| 4382 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4383 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4384 |
+ strings.Contains(errStr, "connection error") || |
|
| 4385 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4386 |
+ p.connSelector.Reset() |
|
| 4387 |
+ } |
|
| 4388 |
+ } |
|
| 4389 |
+ }() |
|
| 4390 |
+ |
|
| 4378 | 4391 |
return NewControlClient(conn).ListTasks(ctx, r) |
| 4379 | 4392 |
} |
| 4380 | 4393 |
|
| ... | ... |
@@ -4391,6 +4469,19 @@ func (p *raftProxyControlServer) RemoveTask(ctx context.Context, r *RemoveTaskRe |
| 4391 | 4391 |
if err != nil {
|
| 4392 | 4392 |
return nil, err |
| 4393 | 4393 |
} |
| 4394 |
+ |
|
| 4395 |
+ defer func() {
|
|
| 4396 |
+ if err != nil {
|
|
| 4397 |
+ errStr := err.Error() |
|
| 4398 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4399 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4400 |
+ strings.Contains(errStr, "connection error") || |
|
| 4401 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4402 |
+ p.connSelector.Reset() |
|
| 4403 |
+ } |
|
| 4404 |
+ } |
|
| 4405 |
+ }() |
|
| 4406 |
+ |
|
| 4394 | 4407 |
return NewControlClient(conn).RemoveTask(ctx, r) |
| 4395 | 4408 |
} |
| 4396 | 4409 |
|
| ... | ... |
@@ -4407,6 +4498,19 @@ func (p *raftProxyControlServer) GetService(ctx context.Context, r *GetServiceRe |
| 4407 | 4407 |
if err != nil {
|
| 4408 | 4408 |
return nil, err |
| 4409 | 4409 |
} |
| 4410 |
+ |
|
| 4411 |
+ defer func() {
|
|
| 4412 |
+ if err != nil {
|
|
| 4413 |
+ errStr := err.Error() |
|
| 4414 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4415 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4416 |
+ strings.Contains(errStr, "connection error") || |
|
| 4417 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4418 |
+ p.connSelector.Reset() |
|
| 4419 |
+ } |
|
| 4420 |
+ } |
|
| 4421 |
+ }() |
|
| 4422 |
+ |
|
| 4410 | 4423 |
return NewControlClient(conn).GetService(ctx, r) |
| 4411 | 4424 |
} |
| 4412 | 4425 |
|
| ... | ... |
@@ -4423,6 +4527,19 @@ func (p *raftProxyControlServer) ListServices(ctx context.Context, r *ListServic |
| 4423 | 4423 |
if err != nil {
|
| 4424 | 4424 |
return nil, err |
| 4425 | 4425 |
} |
| 4426 |
+ |
|
| 4427 |
+ defer func() {
|
|
| 4428 |
+ if err != nil {
|
|
| 4429 |
+ errStr := err.Error() |
|
| 4430 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4431 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4432 |
+ strings.Contains(errStr, "connection error") || |
|
| 4433 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4434 |
+ p.connSelector.Reset() |
|
| 4435 |
+ } |
|
| 4436 |
+ } |
|
| 4437 |
+ }() |
|
| 4438 |
+ |
|
| 4426 | 4439 |
return NewControlClient(conn).ListServices(ctx, r) |
| 4427 | 4440 |
} |
| 4428 | 4441 |
|
| ... | ... |
@@ -4439,6 +4556,19 @@ func (p *raftProxyControlServer) CreateService(ctx context.Context, r *CreateSer |
| 4439 | 4439 |
if err != nil {
|
| 4440 | 4440 |
return nil, err |
| 4441 | 4441 |
} |
| 4442 |
+ |
|
| 4443 |
+ defer func() {
|
|
| 4444 |
+ if err != nil {
|
|
| 4445 |
+ errStr := err.Error() |
|
| 4446 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4447 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4448 |
+ strings.Contains(errStr, "connection error") || |
|
| 4449 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4450 |
+ p.connSelector.Reset() |
|
| 4451 |
+ } |
|
| 4452 |
+ } |
|
| 4453 |
+ }() |
|
| 4454 |
+ |
|
| 4442 | 4455 |
return NewControlClient(conn).CreateService(ctx, r) |
| 4443 | 4456 |
} |
| 4444 | 4457 |
|
| ... | ... |
@@ -4455,6 +4585,19 @@ func (p *raftProxyControlServer) UpdateService(ctx context.Context, r *UpdateSer |
| 4455 | 4455 |
if err != nil {
|
| 4456 | 4456 |
return nil, err |
| 4457 | 4457 |
} |
| 4458 |
+ |
|
| 4459 |
+ defer func() {
|
|
| 4460 |
+ if err != nil {
|
|
| 4461 |
+ errStr := err.Error() |
|
| 4462 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4463 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4464 |
+ strings.Contains(errStr, "connection error") || |
|
| 4465 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4466 |
+ p.connSelector.Reset() |
|
| 4467 |
+ } |
|
| 4468 |
+ } |
|
| 4469 |
+ }() |
|
| 4470 |
+ |
|
| 4458 | 4471 |
return NewControlClient(conn).UpdateService(ctx, r) |
| 4459 | 4472 |
} |
| 4460 | 4473 |
|
| ... | ... |
@@ -4471,6 +4614,19 @@ func (p *raftProxyControlServer) RemoveService(ctx context.Context, r *RemoveSer |
| 4471 | 4471 |
if err != nil {
|
| 4472 | 4472 |
return nil, err |
| 4473 | 4473 |
} |
| 4474 |
+ |
|
| 4475 |
+ defer func() {
|
|
| 4476 |
+ if err != nil {
|
|
| 4477 |
+ errStr := err.Error() |
|
| 4478 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4479 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4480 |
+ strings.Contains(errStr, "connection error") || |
|
| 4481 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4482 |
+ p.connSelector.Reset() |
|
| 4483 |
+ } |
|
| 4484 |
+ } |
|
| 4485 |
+ }() |
|
| 4486 |
+ |
|
| 4474 | 4487 |
return NewControlClient(conn).RemoveService(ctx, r) |
| 4475 | 4488 |
} |
| 4476 | 4489 |
|
| ... | ... |
@@ -4487,6 +4643,19 @@ func (p *raftProxyControlServer) GetNetwork(ctx context.Context, r *GetNetworkRe |
| 4487 | 4487 |
if err != nil {
|
| 4488 | 4488 |
return nil, err |
| 4489 | 4489 |
} |
| 4490 |
+ |
|
| 4491 |
+ defer func() {
|
|
| 4492 |
+ if err != nil {
|
|
| 4493 |
+ errStr := err.Error() |
|
| 4494 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4495 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4496 |
+ strings.Contains(errStr, "connection error") || |
|
| 4497 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4498 |
+ p.connSelector.Reset() |
|
| 4499 |
+ } |
|
| 4500 |
+ } |
|
| 4501 |
+ }() |
|
| 4502 |
+ |
|
| 4490 | 4503 |
return NewControlClient(conn).GetNetwork(ctx, r) |
| 4491 | 4504 |
} |
| 4492 | 4505 |
|
| ... | ... |
@@ -4503,6 +4672,19 @@ func (p *raftProxyControlServer) ListNetworks(ctx context.Context, r *ListNetwor |
| 4503 | 4503 |
if err != nil {
|
| 4504 | 4504 |
return nil, err |
| 4505 | 4505 |
} |
| 4506 |
+ |
|
| 4507 |
+ defer func() {
|
|
| 4508 |
+ if err != nil {
|
|
| 4509 |
+ errStr := err.Error() |
|
| 4510 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4511 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4512 |
+ strings.Contains(errStr, "connection error") || |
|
| 4513 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4514 |
+ p.connSelector.Reset() |
|
| 4515 |
+ } |
|
| 4516 |
+ } |
|
| 4517 |
+ }() |
|
| 4518 |
+ |
|
| 4506 | 4519 |
return NewControlClient(conn).ListNetworks(ctx, r) |
| 4507 | 4520 |
} |
| 4508 | 4521 |
|
| ... | ... |
@@ -4519,6 +4701,19 @@ func (p *raftProxyControlServer) CreateNetwork(ctx context.Context, r *CreateNet |
| 4519 | 4519 |
if err != nil {
|
| 4520 | 4520 |
return nil, err |
| 4521 | 4521 |
} |
| 4522 |
+ |
|
| 4523 |
+ defer func() {
|
|
| 4524 |
+ if err != nil {
|
|
| 4525 |
+ errStr := err.Error() |
|
| 4526 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4527 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4528 |
+ strings.Contains(errStr, "connection error") || |
|
| 4529 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4530 |
+ p.connSelector.Reset() |
|
| 4531 |
+ } |
|
| 4532 |
+ } |
|
| 4533 |
+ }() |
|
| 4534 |
+ |
|
| 4522 | 4535 |
return NewControlClient(conn).CreateNetwork(ctx, r) |
| 4523 | 4536 |
} |
| 4524 | 4537 |
|
| ... | ... |
@@ -4535,6 +4730,19 @@ func (p *raftProxyControlServer) RemoveNetwork(ctx context.Context, r *RemoveNet |
| 4535 | 4535 |
if err != nil {
|
| 4536 | 4536 |
return nil, err |
| 4537 | 4537 |
} |
| 4538 |
+ |
|
| 4539 |
+ defer func() {
|
|
| 4540 |
+ if err != nil {
|
|
| 4541 |
+ errStr := err.Error() |
|
| 4542 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4543 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4544 |
+ strings.Contains(errStr, "connection error") || |
|
| 4545 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4546 |
+ p.connSelector.Reset() |
|
| 4547 |
+ } |
|
| 4548 |
+ } |
|
| 4549 |
+ }() |
|
| 4550 |
+ |
|
| 4538 | 4551 |
return NewControlClient(conn).RemoveNetwork(ctx, r) |
| 4539 | 4552 |
} |
| 4540 | 4553 |
|
| ... | ... |
@@ -4551,6 +4759,19 @@ func (p *raftProxyControlServer) GetCluster(ctx context.Context, r *GetClusterRe |
| 4551 | 4551 |
if err != nil {
|
| 4552 | 4552 |
return nil, err |
| 4553 | 4553 |
} |
| 4554 |
+ |
|
| 4555 |
+ defer func() {
|
|
| 4556 |
+ if err != nil {
|
|
| 4557 |
+ errStr := err.Error() |
|
| 4558 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4559 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4560 |
+ strings.Contains(errStr, "connection error") || |
|
| 4561 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4562 |
+ p.connSelector.Reset() |
|
| 4563 |
+ } |
|
| 4564 |
+ } |
|
| 4565 |
+ }() |
|
| 4566 |
+ |
|
| 4554 | 4567 |
return NewControlClient(conn).GetCluster(ctx, r) |
| 4555 | 4568 |
} |
| 4556 | 4569 |
|
| ... | ... |
@@ -4567,6 +4788,19 @@ func (p *raftProxyControlServer) ListClusters(ctx context.Context, r *ListCluste |
| 4567 | 4567 |
if err != nil {
|
| 4568 | 4568 |
return nil, err |
| 4569 | 4569 |
} |
| 4570 |
+ |
|
| 4571 |
+ defer func() {
|
|
| 4572 |
+ if err != nil {
|
|
| 4573 |
+ errStr := err.Error() |
|
| 4574 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4575 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4576 |
+ strings.Contains(errStr, "connection error") || |
|
| 4577 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4578 |
+ p.connSelector.Reset() |
|
| 4579 |
+ } |
|
| 4580 |
+ } |
|
| 4581 |
+ }() |
|
| 4582 |
+ |
|
| 4570 | 4583 |
return NewControlClient(conn).ListClusters(ctx, r) |
| 4571 | 4584 |
} |
| 4572 | 4585 |
|
| ... | ... |
@@ -4583,6 +4817,19 @@ func (p *raftProxyControlServer) UpdateCluster(ctx context.Context, r *UpdateClu |
| 4583 | 4583 |
if err != nil {
|
| 4584 | 4584 |
return nil, err |
| 4585 | 4585 |
} |
| 4586 |
+ |
|
| 4587 |
+ defer func() {
|
|
| 4588 |
+ if err != nil {
|
|
| 4589 |
+ errStr := err.Error() |
|
| 4590 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 4591 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 4592 |
+ strings.Contains(errStr, "connection error") || |
|
| 4593 |
+ grpc.Code(err) == codes.Internal {
|
|
| 4594 |
+ p.connSelector.Reset() |
|
| 4595 |
+ } |
|
| 4596 |
+ } |
|
| 4597 |
+ }() |
|
| 4598 |
+ |
|
| 4586 | 4599 |
return NewControlClient(conn).UpdateCluster(ctx, r) |
| 4587 | 4600 |
} |
| 4588 | 4601 |
|
| ... | ... |
@@ -1072,12 +1072,12 @@ func encodeVarintDispatcher(data []byte, offset int, v uint64) int {
|
| 1072 | 1072 |
|
| 1073 | 1073 |
type raftProxyDispatcherServer struct {
|
| 1074 | 1074 |
local DispatcherServer |
| 1075 |
- connSelector *raftpicker.ConnSelector |
|
| 1075 |
+ connSelector raftpicker.Interface |
|
| 1076 | 1076 |
cluster raftpicker.RaftCluster |
| 1077 | 1077 |
ctxMods []func(context.Context) (context.Context, error) |
| 1078 | 1078 |
} |
| 1079 | 1079 |
|
| 1080 |
-func NewRaftProxyDispatcherServer(local DispatcherServer, connSelector *raftpicker.ConnSelector, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) DispatcherServer {
|
|
| 1080 |
+func NewRaftProxyDispatcherServer(local DispatcherServer, connSelector raftpicker.Interface, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) DispatcherServer {
|
|
| 1081 | 1081 |
redirectChecker := func(ctx context.Context) (context.Context, error) {
|
| 1082 | 1082 |
s, ok := transport.StreamFromContext(ctx) |
| 1083 | 1083 |
if !ok {
|
| ... | ... |
@@ -1128,6 +1128,19 @@ func (p *raftProxyDispatcherServer) Session(r *SessionRequest, stream Dispatcher |
| 1128 | 1128 |
if err != nil {
|
| 1129 | 1129 |
return err |
| 1130 | 1130 |
} |
| 1131 |
+ |
|
| 1132 |
+ defer func() {
|
|
| 1133 |
+ if err != nil {
|
|
| 1134 |
+ errStr := err.Error() |
|
| 1135 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 1136 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 1137 |
+ strings.Contains(errStr, "connection error") || |
|
| 1138 |
+ grpc.Code(err) == codes.Internal {
|
|
| 1139 |
+ p.connSelector.Reset() |
|
| 1140 |
+ } |
|
| 1141 |
+ } |
|
| 1142 |
+ }() |
|
| 1143 |
+ |
|
| 1131 | 1144 |
clientStream, err := NewDispatcherClient(conn).Session(ctx, r) |
| 1132 | 1145 |
|
| 1133 | 1146 |
if err != nil {
|
| ... | ... |
@@ -1162,6 +1175,19 @@ func (p *raftProxyDispatcherServer) Heartbeat(ctx context.Context, r *HeartbeatR |
| 1162 | 1162 |
if err != nil {
|
| 1163 | 1163 |
return nil, err |
| 1164 | 1164 |
} |
| 1165 |
+ |
|
| 1166 |
+ defer func() {
|
|
| 1167 |
+ if err != nil {
|
|
| 1168 |
+ errStr := err.Error() |
|
| 1169 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 1170 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 1171 |
+ strings.Contains(errStr, "connection error") || |
|
| 1172 |
+ grpc.Code(err) == codes.Internal {
|
|
| 1173 |
+ p.connSelector.Reset() |
|
| 1174 |
+ } |
|
| 1175 |
+ } |
|
| 1176 |
+ }() |
|
| 1177 |
+ |
|
| 1165 | 1178 |
return NewDispatcherClient(conn).Heartbeat(ctx, r) |
| 1166 | 1179 |
} |
| 1167 | 1180 |
|
| ... | ... |
@@ -1178,6 +1204,19 @@ func (p *raftProxyDispatcherServer) UpdateTaskStatus(ctx context.Context, r *Upd |
| 1178 | 1178 |
if err != nil {
|
| 1179 | 1179 |
return nil, err |
| 1180 | 1180 |
} |
| 1181 |
+ |
|
| 1182 |
+ defer func() {
|
|
| 1183 |
+ if err != nil {
|
|
| 1184 |
+ errStr := err.Error() |
|
| 1185 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 1186 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 1187 |
+ strings.Contains(errStr, "connection error") || |
|
| 1188 |
+ grpc.Code(err) == codes.Internal {
|
|
| 1189 |
+ p.connSelector.Reset() |
|
| 1190 |
+ } |
|
| 1191 |
+ } |
|
| 1192 |
+ }() |
|
| 1193 |
+ |
|
| 1181 | 1194 |
return NewDispatcherClient(conn).UpdateTaskStatus(ctx, r) |
| 1182 | 1195 |
} |
| 1183 | 1196 |
|
| ... | ... |
@@ -1194,6 +1233,19 @@ func (p *raftProxyDispatcherServer) Tasks(r *TasksRequest, stream Dispatcher_Tas |
| 1194 | 1194 |
if err != nil {
|
| 1195 | 1195 |
return err |
| 1196 | 1196 |
} |
| 1197 |
+ |
|
| 1198 |
+ defer func() {
|
|
| 1199 |
+ if err != nil {
|
|
| 1200 |
+ errStr := err.Error() |
|
| 1201 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 1202 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 1203 |
+ strings.Contains(errStr, "connection error") || |
|
| 1204 |
+ grpc.Code(err) == codes.Internal {
|
|
| 1205 |
+ p.connSelector.Reset() |
|
| 1206 |
+ } |
|
| 1207 |
+ } |
|
| 1208 |
+ }() |
|
| 1209 |
+ |
|
| 1197 | 1210 |
clientStream, err := NewDispatcherClient(conn).Tasks(ctx, r) |
| 1198 | 1211 |
|
| 1199 | 1212 |
if err != nil {
|
| ... | ... |
@@ -19,3 +19,11 @@ func TasksEqualStable(a, b *api.Task) bool {
|
| 19 | 19 |
|
| 20 | 20 |
return reflect.DeepEqual(©A, ©B) |
| 21 | 21 |
} |
| 22 |
+ |
|
| 23 |
+// TaskStatusesEqualStable compares the task status excluding timestamp fields. |
|
| 24 |
+func TaskStatusesEqualStable(a, b *api.TaskStatus) bool {
|
|
| 25 |
+ copyA, copyB := *a, *b |
|
| 26 |
+ |
|
| 27 |
+ copyA.Timestamp, copyB.Timestamp = nil, nil |
|
| 28 |
+ return reflect.DeepEqual(©A, ©B) |
|
| 29 |
+} |
| ... | ... |
@@ -319,12 +319,12 @@ func encodeVarintHealth(data []byte, offset int, v uint64) int {
|
| 319 | 319 |
|
| 320 | 320 |
type raftProxyHealthServer struct {
|
| 321 | 321 |
local HealthServer |
| 322 |
- connSelector *raftpicker.ConnSelector |
|
| 322 |
+ connSelector raftpicker.Interface |
|
| 323 | 323 |
cluster raftpicker.RaftCluster |
| 324 | 324 |
ctxMods []func(context.Context) (context.Context, error) |
| 325 | 325 |
} |
| 326 | 326 |
|
| 327 |
-func NewRaftProxyHealthServer(local HealthServer, connSelector *raftpicker.ConnSelector, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) HealthServer {
|
|
| 327 |
+func NewRaftProxyHealthServer(local HealthServer, connSelector raftpicker.Interface, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) HealthServer {
|
|
| 328 | 328 |
redirectChecker := func(ctx context.Context) (context.Context, error) {
|
| 329 | 329 |
s, ok := transport.StreamFromContext(ctx) |
| 330 | 330 |
if !ok {
|
| ... | ... |
@@ -375,6 +375,19 @@ func (p *raftProxyHealthServer) Check(ctx context.Context, r *HealthCheckRequest |
| 375 | 375 |
if err != nil {
|
| 376 | 376 |
return nil, err |
| 377 | 377 |
} |
| 378 |
+ |
|
| 379 |
+ defer func() {
|
|
| 380 |
+ if err != nil {
|
|
| 381 |
+ errStr := err.Error() |
|
| 382 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 383 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 384 |
+ strings.Contains(errStr, "connection error") || |
|
| 385 |
+ grpc.Code(err) == codes.Internal {
|
|
| 386 |
+ p.connSelector.Reset() |
|
| 387 |
+ } |
|
| 388 |
+ } |
|
| 389 |
+ }() |
|
| 390 |
+ |
|
| 378 | 391 |
return NewHealthClient(conn).Check(ctx, r) |
| 379 | 392 |
} |
| 380 | 393 |
|
| ... | ... |
@@ -1438,12 +1438,12 @@ func encodeVarintRaft(data []byte, offset int, v uint64) int {
|
| 1438 | 1438 |
|
| 1439 | 1439 |
type raftProxyRaftServer struct {
|
| 1440 | 1440 |
local RaftServer |
| 1441 |
- connSelector *raftpicker.ConnSelector |
|
| 1441 |
+ connSelector raftpicker.Interface |
|
| 1442 | 1442 |
cluster raftpicker.RaftCluster |
| 1443 | 1443 |
ctxMods []func(context.Context) (context.Context, error) |
| 1444 | 1444 |
} |
| 1445 | 1445 |
|
| 1446 |
-func NewRaftProxyRaftServer(local RaftServer, connSelector *raftpicker.ConnSelector, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) RaftServer {
|
|
| 1446 |
+func NewRaftProxyRaftServer(local RaftServer, connSelector raftpicker.Interface, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) RaftServer {
|
|
| 1447 | 1447 |
redirectChecker := func(ctx context.Context) (context.Context, error) {
|
| 1448 | 1448 |
s, ok := transport.StreamFromContext(ctx) |
| 1449 | 1449 |
if !ok {
|
| ... | ... |
@@ -1494,6 +1494,19 @@ func (p *raftProxyRaftServer) ProcessRaftMessage(ctx context.Context, r *Process |
| 1494 | 1494 |
if err != nil {
|
| 1495 | 1495 |
return nil, err |
| 1496 | 1496 |
} |
| 1497 |
+ |
|
| 1498 |
+ defer func() {
|
|
| 1499 |
+ if err != nil {
|
|
| 1500 |
+ errStr := err.Error() |
|
| 1501 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 1502 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 1503 |
+ strings.Contains(errStr, "connection error") || |
|
| 1504 |
+ grpc.Code(err) == codes.Internal {
|
|
| 1505 |
+ p.connSelector.Reset() |
|
| 1506 |
+ } |
|
| 1507 |
+ } |
|
| 1508 |
+ }() |
|
| 1509 |
+ |
|
| 1497 | 1510 |
return NewRaftClient(conn).ProcessRaftMessage(ctx, r) |
| 1498 | 1511 |
} |
| 1499 | 1512 |
|
| ... | ... |
@@ -1510,17 +1523,30 @@ func (p *raftProxyRaftServer) ResolveAddress(ctx context.Context, r *ResolveAddr |
| 1510 | 1510 |
if err != nil {
|
| 1511 | 1511 |
return nil, err |
| 1512 | 1512 |
} |
| 1513 |
+ |
|
| 1514 |
+ defer func() {
|
|
| 1515 |
+ if err != nil {
|
|
| 1516 |
+ errStr := err.Error() |
|
| 1517 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 1518 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 1519 |
+ strings.Contains(errStr, "connection error") || |
|
| 1520 |
+ grpc.Code(err) == codes.Internal {
|
|
| 1521 |
+ p.connSelector.Reset() |
|
| 1522 |
+ } |
|
| 1523 |
+ } |
|
| 1524 |
+ }() |
|
| 1525 |
+ |
|
| 1513 | 1526 |
return NewRaftClient(conn).ResolveAddress(ctx, r) |
| 1514 | 1527 |
} |
| 1515 | 1528 |
|
| 1516 | 1529 |
type raftProxyRaftMembershipServer struct {
|
| 1517 | 1530 |
local RaftMembershipServer |
| 1518 |
- connSelector *raftpicker.ConnSelector |
|
| 1531 |
+ connSelector raftpicker.Interface |
|
| 1519 | 1532 |
cluster raftpicker.RaftCluster |
| 1520 | 1533 |
ctxMods []func(context.Context) (context.Context, error) |
| 1521 | 1534 |
} |
| 1522 | 1535 |
|
| 1523 |
-func NewRaftProxyRaftMembershipServer(local RaftMembershipServer, connSelector *raftpicker.ConnSelector, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) RaftMembershipServer {
|
|
| 1536 |
+func NewRaftProxyRaftMembershipServer(local RaftMembershipServer, connSelector raftpicker.Interface, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) RaftMembershipServer {
|
|
| 1524 | 1537 |
redirectChecker := func(ctx context.Context) (context.Context, error) {
|
| 1525 | 1538 |
s, ok := transport.StreamFromContext(ctx) |
| 1526 | 1539 |
if !ok {
|
| ... | ... |
@@ -1571,6 +1597,19 @@ func (p *raftProxyRaftMembershipServer) Join(ctx context.Context, r *JoinRequest |
| 1571 | 1571 |
if err != nil {
|
| 1572 | 1572 |
return nil, err |
| 1573 | 1573 |
} |
| 1574 |
+ |
|
| 1575 |
+ defer func() {
|
|
| 1576 |
+ if err != nil {
|
|
| 1577 |
+ errStr := err.Error() |
|
| 1578 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 1579 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 1580 |
+ strings.Contains(errStr, "connection error") || |
|
| 1581 |
+ grpc.Code(err) == codes.Internal {
|
|
| 1582 |
+ p.connSelector.Reset() |
|
| 1583 |
+ } |
|
| 1584 |
+ } |
|
| 1585 |
+ }() |
|
| 1586 |
+ |
|
| 1574 | 1587 |
return NewRaftMembershipClient(conn).Join(ctx, r) |
| 1575 | 1588 |
} |
| 1576 | 1589 |
|
| ... | ... |
@@ -1587,6 +1626,19 @@ func (p *raftProxyRaftMembershipServer) Leave(ctx context.Context, r *LeaveReque |
| 1587 | 1587 |
if err != nil {
|
| 1588 | 1588 |
return nil, err |
| 1589 | 1589 |
} |
| 1590 |
+ |
|
| 1591 |
+ defer func() {
|
|
| 1592 |
+ if err != nil {
|
|
| 1593 |
+ errStr := err.Error() |
|
| 1594 |
+ if strings.Contains(errStr, grpc.ErrClientConnClosing.Error()) || |
|
| 1595 |
+ strings.Contains(errStr, grpc.ErrClientConnTimeout.Error()) || |
|
| 1596 |
+ strings.Contains(errStr, "connection error") || |
|
| 1597 |
+ grpc.Code(err) == codes.Internal {
|
|
| 1598 |
+ p.connSelector.Reset() |
|
| 1599 |
+ } |
|
| 1600 |
+ } |
|
| 1601 |
+ }() |
|
| 1602 |
+ |
|
| 1590 | 1603 |
return NewRaftMembershipClient(conn).Leave(ctx, r) |
| 1591 | 1604 |
} |
| 1592 | 1605 |
|
| ... | ... |
@@ -432,8 +432,14 @@ type ContainerSpec struct {
|
| 432 | 432 |
// Dir defines the working directory to set for the container process. |
| 433 | 433 |
Dir string `protobuf:"bytes,6,opt,name=dir,proto3" json:"dir,omitempty"` |
| 434 | 434 |
// User specifies the user that should be employed to run the container. |
| 435 |
- User string `protobuf:"bytes,7,opt,name=user,proto3" json:"user,omitempty"` |
|
| 436 |
- Mounts []Mount `protobuf:"bytes,8,rep,name=mounts" json:"mounts"` |
|
| 435 |
+ // |
|
| 436 |
+ // Note that the primary group may be specified by appending the group name |
|
| 437 |
+ // or id to the user name, separated by a `:`. This syntax is |
|
| 438 |
+ // `<user>:<group>`. |
|
| 439 |
+ User string `protobuf:"bytes,7,opt,name=user,proto3" json:"user,omitempty"` |
|
| 440 |
+ // Groups specifies supplementary groups available to the user. |
|
| 441 |
+ Groups []string `protobuf:"bytes,11,rep,name=groups" json:"groups,omitempty"` |
|
| 442 |
+ Mounts []Mount `protobuf:"bytes,8,rep,name=mounts" json:"mounts"` |
|
| 437 | 443 |
// StopGracePeriod the grace period for stopping the container before |
| 438 | 444 |
// forcefully killing the container. |
| 439 | 445 |
StopGracePeriod *docker_swarmkit_v11.Duration `protobuf:"bytes,9,opt,name=stop_grace_period,json=stopGracePeriod" json:"stop_grace_period,omitempty"` |
| ... | ... |
@@ -688,6 +694,13 @@ func (m *ContainerSpec) Copy() *ContainerSpec {
|
| 688 | 688 |
} |
| 689 | 689 |
} |
| 690 | 690 |
|
| 691 |
+ if m.Groups != nil {
|
|
| 692 |
+ o.Groups = make([]string, 0, len(m.Groups)) |
|
| 693 |
+ for _, v := range m.Groups {
|
|
| 694 |
+ o.Groups = append(o.Groups, v) |
|
| 695 |
+ } |
|
| 696 |
+ } |
|
| 697 |
+ |
|
| 691 | 698 |
if m.Mounts != nil {
|
| 692 | 699 |
o.Mounts = make([]Mount, 0, len(m.Mounts)) |
| 693 | 700 |
for _, v := range m.Mounts {
|
| ... | ... |
@@ -881,7 +894,7 @@ func (this *ContainerSpec) GoString() string {
|
| 881 | 881 |
if this == nil {
|
| 882 | 882 |
return "nil" |
| 883 | 883 |
} |
| 884 |
- s := make([]string, 0, 14) |
|
| 884 |
+ s := make([]string, 0, 15) |
|
| 885 | 885 |
s = append(s, "&api.ContainerSpec{")
|
| 886 | 886 |
s = append(s, "Image: "+fmt.Sprintf("%#v", this.Image)+",\n")
|
| 887 | 887 |
keysForLabels := make([]string, 0, len(this.Labels)) |
| ... | ... |
@@ -902,6 +915,7 @@ func (this *ContainerSpec) GoString() string {
|
| 902 | 902 |
s = append(s, "Env: "+fmt.Sprintf("%#v", this.Env)+",\n")
|
| 903 | 903 |
s = append(s, "Dir: "+fmt.Sprintf("%#v", this.Dir)+",\n")
|
| 904 | 904 |
s = append(s, "User: "+fmt.Sprintf("%#v", this.User)+",\n")
|
| 905 |
+ s = append(s, "Groups: "+fmt.Sprintf("%#v", this.Groups)+",\n")
|
|
| 905 | 906 |
if this.Mounts != nil {
|
| 906 | 907 |
s = append(s, "Mounts: "+fmt.Sprintf("%#v", this.Mounts)+",\n")
|
| 907 | 908 |
} |
| ... | ... |
@@ -1424,6 +1438,21 @@ func (m *ContainerSpec) MarshalTo(data []byte) (int, error) {
|
| 1424 | 1424 |
} |
| 1425 | 1425 |
i += n16 |
| 1426 | 1426 |
} |
| 1427 |
+ if len(m.Groups) > 0 {
|
|
| 1428 |
+ for _, s := range m.Groups {
|
|
| 1429 |
+ data[i] = 0x5a |
|
| 1430 |
+ i++ |
|
| 1431 |
+ l = len(s) |
|
| 1432 |
+ for l >= 1<<7 {
|
|
| 1433 |
+ data[i] = uint8(uint64(l)&0x7f | 0x80) |
|
| 1434 |
+ l >>= 7 |
|
| 1435 |
+ i++ |
|
| 1436 |
+ } |
|
| 1437 |
+ data[i] = uint8(l) |
|
| 1438 |
+ i++ |
|
| 1439 |
+ i += copy(data[i:], s) |
|
| 1440 |
+ } |
|
| 1441 |
+ } |
|
| 1427 | 1442 |
return i, nil |
| 1428 | 1443 |
} |
| 1429 | 1444 |
|
| ... | ... |
@@ -1838,6 +1867,12 @@ func (m *ContainerSpec) Size() (n int) {
|
| 1838 | 1838 |
l = m.PullOptions.Size() |
| 1839 | 1839 |
n += 1 + l + sovSpecs(uint64(l)) |
| 1840 | 1840 |
} |
| 1841 |
+ if len(m.Groups) > 0 {
|
|
| 1842 |
+ for _, s := range m.Groups {
|
|
| 1843 |
+ l = len(s) |
|
| 1844 |
+ n += 1 + l + sovSpecs(uint64(l)) |
|
| 1845 |
+ } |
|
| 1846 |
+ } |
|
| 1841 | 1847 |
return n |
| 1842 | 1848 |
} |
| 1843 | 1849 |
|
| ... | ... |
@@ -2048,6 +2083,7 @@ func (this *ContainerSpec) String() string {
|
| 2048 | 2048 |
`Mounts:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Mounts), "Mount", "Mount", 1), `&`, ``, 1) + `,`,
|
| 2049 | 2049 |
`StopGracePeriod:` + strings.Replace(fmt.Sprintf("%v", this.StopGracePeriod), "Duration", "docker_swarmkit_v11.Duration", 1) + `,`,
|
| 2050 | 2050 |
`PullOptions:` + strings.Replace(fmt.Sprintf("%v", this.PullOptions), "ContainerSpec_PullOptions", "ContainerSpec_PullOptions", 1) + `,`,
|
| 2051 |
+ `Groups:` + fmt.Sprintf("%v", this.Groups) + `,`,
|
|
| 2051 | 2052 |
`}`, |
| 2052 | 2053 |
}, "") |
| 2053 | 2054 |
return s |
| ... | ... |
@@ -3371,6 +3407,35 @@ func (m *ContainerSpec) Unmarshal(data []byte) error {
|
| 3371 | 3371 |
return err |
| 3372 | 3372 |
} |
| 3373 | 3373 |
iNdEx = postIndex |
| 3374 |
+ case 11: |
|
| 3375 |
+ if wireType != 2 {
|
|
| 3376 |
+ return fmt.Errorf("proto: wrong wireType = %d for field Groups", wireType)
|
|
| 3377 |
+ } |
|
| 3378 |
+ var stringLen uint64 |
|
| 3379 |
+ for shift := uint(0); ; shift += 7 {
|
|
| 3380 |
+ if shift >= 64 {
|
|
| 3381 |
+ return ErrIntOverflowSpecs |
|
| 3382 |
+ } |
|
| 3383 |
+ if iNdEx >= l {
|
|
| 3384 |
+ return io.ErrUnexpectedEOF |
|
| 3385 |
+ } |
|
| 3386 |
+ b := data[iNdEx] |
|
| 3387 |
+ iNdEx++ |
|
| 3388 |
+ stringLen |= (uint64(b) & 0x7F) << shift |
|
| 3389 |
+ if b < 0x80 {
|
|
| 3390 |
+ break |
|
| 3391 |
+ } |
|
| 3392 |
+ } |
|
| 3393 |
+ intStringLen := int(stringLen) |
|
| 3394 |
+ if intStringLen < 0 {
|
|
| 3395 |
+ return ErrInvalidLengthSpecs |
|
| 3396 |
+ } |
|
| 3397 |
+ postIndex := iNdEx + intStringLen |
|
| 3398 |
+ if postIndex > l {
|
|
| 3399 |
+ return io.ErrUnexpectedEOF |
|
| 3400 |
+ } |
|
| 3401 |
+ m.Groups = append(m.Groups, string(data[iNdEx:postIndex])) |
|
| 3402 |
+ iNdEx = postIndex |
|
| 3374 | 3403 |
default: |
| 3375 | 3404 |
iNdEx = preIndex |
| 3376 | 3405 |
skippy, err := skipSpecs(data[iNdEx:]) |
| ... | ... |
@@ -4123,89 +4188,89 @@ var ( |
| 4123 | 4123 |
) |
| 4124 | 4124 |
|
| 4125 | 4125 |
var fileDescriptorSpecs = []byte{
|
| 4126 |
- // 1332 bytes of a gzipped FileDescriptorProto |
|
| 4126 |
+ // 1344 bytes of a gzipped FileDescriptorProto |
|
| 4127 | 4127 |
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x57, 0x4f, 0x6f, 0x1b, 0x45, |
| 4128 | 4128 |
0x14, 0x8f, 0x93, 0x8d, 0xe3, 0xbc, 0x75, 0xda, 0x74, 0x54, 0x5a, 0xd7, 0x2d, 0x49, 0x6a, 0x0a, |
| 4129 |
- 0x14, 0x24, 0x1c, 0x30, 0xa8, 0x7f, 0xf8, 0x23, 0x70, 0x6c, 0x93, 0x86, 0x92, 0x74, 0x35, 0x69, |
|
| 4130 |
- 0x2b, 0x71, 0xb2, 0x26, 0xbb, 0x53, 0x67, 0x95, 0xf5, 0xee, 0x32, 0x3b, 0xeb, 0x2a, 0x37, 0x8e, |
|
| 4131 |
- 0x15, 0x07, 0x6e, 0x70, 0xe3, 0x84, 0xc4, 0x47, 0xe0, 0x33, 0xf4, 0xc8, 0x05, 0x89, 0x53, 0x45, |
|
| 4132 |
- 0xfb, 0x09, 0x90, 0xf8, 0x02, 0xbc, 0x99, 0x1d, 0xdb, 0x6b, 0xba, 0x69, 0x39, 0xf4, 0x60, 0x69, |
|
| 4133 |
- 0xe6, 0xcd, 0xef, 0xf7, 0x66, 0xe6, 0xbd, 0xdf, 0xbc, 0xb7, 0x06, 0x3b, 0x89, 0xb9, 0x9b, 0x34, |
|
| 4134 |
- 0x63, 0x11, 0xc9, 0x88, 0x10, 0x2f, 0x72, 0x8f, 0xb8, 0x68, 0x26, 0x0f, 0x99, 0x18, 0x1e, 0xf9, |
|
| 4135 |
- 0xb2, 0x39, 0xfa, 0xa0, 0x6e, 0xcb, 0xe3, 0x98, 0x1b, 0x40, 0xfd, 0xec, 0x20, 0x1a, 0x44, 0x7a, |
|
| 4136 |
- 0xb8, 0xa9, 0x46, 0xc6, 0x7a, 0xde, 0x4b, 0x05, 0x93, 0x7e, 0x14, 0x6e, 0x8e, 0x07, 0xd9, 0x42, |
|
| 4137 |
- 0xe3, 0x07, 0x0b, 0x2a, 0x7b, 0x91, 0xc7, 0xf7, 0x71, 0x0f, 0xb2, 0x0d, 0x36, 0x0b, 0xc3, 0x48, |
|
| 4138 |
- 0x6a, 0x40, 0x52, 0x2b, 0x6d, 0x94, 0xae, 0xda, 0xad, 0xf5, 0xe6, 0xf3, 0x5b, 0x36, 0xdb, 0x53, |
|
| 4139 |
- 0xd8, 0x96, 0xf5, 0xf8, 0xc9, 0xfa, 0x1c, 0xcd, 0x33, 0xc9, 0xfb, 0x60, 0x89, 0x28, 0xe0, 0xb5, |
|
| 4140 |
- 0x79, 0xf4, 0x70, 0xaa, 0x75, 0xa9, 0xc8, 0x83, 0xda, 0x94, 0x22, 0x86, 0x6a, 0x24, 0x6e, 0x0d, |
|
| 4141 |
- 0x43, 0x3e, 0x3c, 0xe0, 0x22, 0x39, 0xf4, 0xe3, 0xda, 0x82, 0xe6, 0xbd, 0x7d, 0x12, 0x4f, 0x1d, |
|
| 4142 |
- 0xb6, 0xb9, 0x3b, 0x81, 0xd3, 0x1c, 0x95, 0xec, 0x42, 0x95, 0x8d, 0x98, 0x1f, 0xb0, 0x03, 0x3f, |
|
| 4143 |
- 0xf0, 0xe5, 0x71, 0xcd, 0xd2, 0xae, 0xde, 0x79, 0xa1, 0xab, 0x76, 0x8e, 0x40, 0x67, 0xe8, 0x0d, |
|
| 4144 |
- 0x0f, 0x60, 0xba, 0x11, 0x79, 0x0b, 0x96, 0x9c, 0xde, 0x5e, 0x77, 0x67, 0x6f, 0x7b, 0x75, 0xae, |
|
| 4145 |
- 0x7e, 0xe1, 0xfb, 0x9f, 0x37, 0x5e, 0x53, 0x3e, 0xa6, 0x00, 0x87, 0x87, 0x9e, 0x1f, 0x0e, 0xc8, |
|
| 4146 |
- 0x55, 0xa8, 0xb4, 0x3b, 0x9d, 0x9e, 0x73, 0xb7, 0xd7, 0x5d, 0x2d, 0xd5, 0xeb, 0x08, 0x3c, 0x37, |
|
| 4147 |
- 0x0b, 0x6c, 0xbb, 0x2e, 0x8f, 0x25, 0xf7, 0xea, 0xd6, 0xa3, 0x5f, 0xd6, 0xe6, 0x1a, 0x8f, 0x4a, |
|
| 4148 |
- 0x50, 0xcd, 0x1f, 0x02, 0x37, 0x2a, 0xb7, 0x3b, 0x77, 0x77, 0xee, 0xf7, 0x70, 0x9f, 0x09, 0x3d, |
|
| 4149 |
- 0x8f, 0x68, 0xbb, 0xd2, 0x1f, 0x71, 0x72, 0x05, 0x16, 0x9d, 0xf6, 0xbd, 0xfd, 0x1e, 0xee, 0x32, |
|
| 4150 |
- 0x39, 0x4e, 0x1e, 0xe6, 0xb0, 0x34, 0xd1, 0xa8, 0x2e, 0x6d, 0xef, 0xec, 0xad, 0xce, 0x17, 0xa3, |
|
| 4151 |
- 0xba, 0x82, 0xf9, 0xa1, 0x39, 0xca, 0x6f, 0x16, 0xd8, 0xfb, 0x5c, 0x8c, 0x7c, 0xf7, 0x15, 0x6b, |
|
| 4152 |
- 0xe2, 0x1a, 0x58, 0x92, 0x25, 0x47, 0x5a, 0x13, 0x76, 0xb1, 0x26, 0xee, 0xe2, 0xba, 0xda, 0xd4, |
|
| 4153 |
- 0xd0, 0x35, 0x5e, 0x29, 0x43, 0xf0, 0x38, 0xf0, 0x5d, 0x86, 0xf1, 0xd2, 0xca, 0xb0, 0x5b, 0x6f, |
|
| 4154 |
- 0x16, 0xb1, 0xe9, 0x04, 0x65, 0xce, 0x7f, 0x6b, 0x8e, 0xe6, 0xa8, 0xe4, 0x13, 0x28, 0x0f, 0x82, |
|
| 4155 |
- 0xe8, 0x80, 0x05, 0x5a, 0x13, 0x76, 0xeb, 0x72, 0x91, 0x93, 0x6d, 0x8d, 0x98, 0x3a, 0x30, 0x14, |
|
| 4156 |
- 0x72, 0x03, 0xca, 0x69, 0xec, 0xa1, 0x9f, 0x5a, 0x59, 0x93, 0x37, 0x8a, 0xc8, 0xf7, 0x34, 0xa2, |
|
| 4157 |
- 0x13, 0x85, 0x0f, 0xfc, 0x01, 0x35, 0x78, 0xb2, 0x0f, 0x95, 0x90, 0xcb, 0x87, 0x91, 0x38, 0x4a, |
|
| 4158 |
- 0x6a, 0x4b, 0x1b, 0x0b, 0xc8, 0xbd, 0x5e, 0xc4, 0xcd, 0xc5, 0xbc, 0xb9, 0x97, 0xe1, 0xdb, 0x52, |
|
| 4159 |
- 0x32, 0xf7, 0x70, 0xc8, 0x43, 0x69, 0x5c, 0x4e, 0x1c, 0x91, 0x4f, 0xa1, 0x82, 0x52, 0x8b, 0x23, |
|
| 4160 |
- 0x3f, 0x94, 0xb5, 0xca, 0xc9, 0x07, 0xea, 0x19, 0x8c, 0xf2, 0x4a, 0x27, 0x8c, 0xfa, 0x6d, 0x38, |
|
| 4161 |
- 0x7f, 0xc2, 0x16, 0xe4, 0x1c, 0x94, 0x25, 0x13, 0x03, 0x2e, 0x75, 0xa6, 0x97, 0xa9, 0x99, 0x91, |
|
| 4162 |
- 0x1a, 0x2c, 0xb1, 0xc0, 0x67, 0x09, 0x4f, 0x30, 0x81, 0x0b, 0xb8, 0x30, 0x9e, 0x6e, 0x95, 0xc1, |
|
| 4163 |
- 0x1a, 0xa2, 0x9e, 0x1a, 0x9b, 0x70, 0xe6, 0xb9, 0x0c, 0x90, 0x3a, 0x54, 0x4c, 0x06, 0x32, 0xe9, |
|
| 4164 |
- 0x58, 0x74, 0x32, 0x6f, 0x9c, 0x86, 0x95, 0x99, 0x68, 0x37, 0xfe, 0x98, 0x87, 0xca, 0x58, 0x02, |
|
| 4165 |
- 0xa4, 0x0d, 0xcb, 0x6e, 0x14, 0x4a, 0x14, 0x26, 0x17, 0x46, 0x75, 0x85, 0x09, 0xeb, 0x8c, 0x41, |
|
| 4166 |
- 0x8a, 0x85, 0x09, 0x9b, 0xb2, 0xc8, 0x97, 0xb0, 0x2c, 0x78, 0x12, 0xa5, 0xc2, 0xd5, 0xa7, 0x56, |
|
| 4167 |
- 0x2e, 0xae, 0x16, 0x0b, 0x27, 0x03, 0x51, 0xfe, 0x6d, 0xea, 0x0b, 0xae, 0xa2, 0x91, 0xd0, 0x29, |
|
| 4168 |
- 0x15, 0x85, 0xb3, 0x84, 0x13, 0x0c, 0x84, 0x7c, 0x91, 0x72, 0x68, 0x06, 0x71, 0x22, 0xbc, 0xdd, |
|
| 4169 |
- 0x31, 0x1d, 0x33, 0x90, 0xbc, 0x1c, 0x07, 0xcc, 0xd5, 0x5e, 0x6b, 0x8b, 0x9a, 0xfe, 0x7a, 0x11, |
|
| 4170 |
- 0xdd, 0x19, 0x83, 0xe8, 0x14, 0x4f, 0x6e, 0x02, 0x04, 0xd1, 0xa0, 0xef, 0x09, 0x7c, 0xeb, 0xc2, |
|
| 4171 |
- 0x28, 0xaf, 0x5e, 0xc4, 0xee, 0x6a, 0x04, 0x5d, 0x46, 0x74, 0x36, 0xdc, 0x5a, 0xc6, 0x43, 0xa7, |
|
| 4172 |
- 0xa1, 0xf4, 0x87, 0xbc, 0xf1, 0x93, 0x05, 0x2b, 0x33, 0x61, 0x22, 0x67, 0x61, 0xd1, 0x1f, 0xb2, |
|
| 4173 |
- 0x01, 0x37, 0x49, 0xce, 0x26, 0xa4, 0x07, 0x65, 0xac, 0x08, 0x3c, 0xc8, 0x52, 0x6c, 0xb7, 0xde, |
|
| 4174 |
- 0x7b, 0x69, 0xbc, 0x9b, 0x5f, 0x6b, 0x7c, 0x2f, 0x94, 0xe2, 0x98, 0x1a, 0xb2, 0x92, 0x8a, 0x1b, |
|
| 4175 |
- 0x0d, 0x87, 0x2c, 0x54, 0xaf, 0x55, 0x4b, 0xc5, 0x4c, 0x09, 0x01, 0x0b, 0xd5, 0x94, 0x60, 0x14, |
|
| 4176 |
- 0x95, 0x59, 0x8f, 0xc9, 0x2a, 0x2c, 0xf0, 0x70, 0x84, 0x91, 0x51, 0x26, 0x35, 0x54, 0x16, 0xcf, |
|
| 4177 |
- 0xcf, 0x6e, 0x8b, 0x16, 0x1c, 0x2a, 0x1e, 0x96, 0x31, 0x81, 0xcf, 0x47, 0x99, 0xf4, 0x98, 0x5c, |
|
| 4178 |
- 0x87, 0xf2, 0x30, 0xc2, 0x0b, 0x26, 0xa8, 0x7f, 0x75, 0xd8, 0x0b, 0x45, 0x87, 0xdd, 0x55, 0x08, |
|
| 4179 |
- 0x53, 0x4d, 0x0c, 0x9c, 0xdc, 0x82, 0x33, 0x89, 0x8c, 0xe2, 0xfe, 0x40, 0x60, 0x94, 0xfb, 0x31, |
|
| 4180 |
- 0x17, 0x7e, 0xe4, 0xd5, 0x96, 0x4f, 0x2e, 0x4a, 0x5d, 0xd3, 0x30, 0xe9, 0x69, 0x45, 0xdb, 0x56, |
|
| 4181 |
- 0x2c, 0x47, 0x93, 0x88, 0x03, 0xd5, 0x38, 0x0d, 0x82, 0x7e, 0x14, 0x67, 0xb5, 0x11, 0xb4, 0x93, |
|
| 4182 |
- 0xff, 0x11, 0x35, 0x07, 0x59, 0x77, 0x32, 0x12, 0xb5, 0xe3, 0xe9, 0xa4, 0x7e, 0x13, 0xec, 0x5c, |
|
| 4183 |
- 0x44, 0x55, 0x24, 0x8e, 0xf8, 0xb1, 0x49, 0x92, 0x1a, 0xaa, 0xc4, 0x8d, 0x58, 0x90, 0x66, 0x9d, |
|
| 4184 |
- 0x15, 0x13, 0xa7, 0x27, 0x1f, 0xcf, 0xdf, 0x28, 0xd5, 0x5b, 0x60, 0xe7, 0xdc, 0x92, 0x37, 0x60, |
|
| 4185 |
- 0x45, 0xf0, 0x81, 0x9f, 0xa0, 0x9b, 0x3e, 0x4b, 0xe5, 0x61, 0xed, 0x0b, 0x4d, 0xa8, 0x8e, 0x8d, |
|
| 4186 |
- 0x6d, 0xb4, 0x35, 0xfe, 0xc1, 0xb6, 0x93, 0x2f, 0x11, 0xa4, 0x93, 0xbd, 0x65, 0xbd, 0xe3, 0xa9, |
|
| 4187 |
- 0xd6, 0xe6, 0xcb, 0x4a, 0x8a, 0x7e, 0x39, 0x41, 0xaa, 0x76, 0xdc, 0x55, 0xed, 0x5c, 0x93, 0xc9, |
|
| 4188 |
- 0x47, 0xb0, 0x18, 0x47, 0x42, 0x8e, 0x55, 0xb4, 0x56, 0xa8, 0x76, 0x04, 0x98, 0xa2, 0x96, 0x81, |
|
| 4189 |
- 0x1b, 0x87, 0x70, 0x6a, 0xd6, 0x1b, 0x76, 0xad, 0x85, 0xfb, 0x3b, 0x0e, 0x36, 0xc0, 0x8b, 0xd8, |
|
| 4190 |
- 0xb3, 0xce, 0xcf, 0x2e, 0xde, 0xf7, 0x85, 0x4c, 0x59, 0xb0, 0xe3, 0x90, 0x77, 0xb1, 0xb7, 0xed, |
|
| 4191 |
- 0xed, 0x53, 0x8a, 0x1d, 0x70, 0x1d, 0x71, 0x17, 0x67, 0x71, 0x6a, 0x09, 0xd3, 0xee, 0xd1, 0xe8, |
|
| 4192 |
- 0x60, 0xd2, 0xe1, 0x7e, 0x9c, 0x07, 0xdb, 0x94, 0xbf, 0x57, 0xdb, 0xe1, 0x3e, 0x87, 0x95, 0xec, |
|
| 4193 |
- 0xa5, 0xf6, 0x5d, 0x7d, 0x35, 0x53, 0x73, 0x5e, 0xf4, 0x60, 0xab, 0x19, 0xc1, 0x14, 0xdf, 0xcb, |
|
| 4194 |
- 0x50, 0xf5, 0xe3, 0xd1, 0xb5, 0x3e, 0x0f, 0xd9, 0x41, 0x60, 0x9a, 0x5d, 0x85, 0xda, 0xca, 0xd6, |
|
| 4195 |
- 0xcb, 0x4c, 0xaa, 0xa0, 0x62, 0xf0, 0xb9, 0x08, 0x4d, 0x1b, 0xab, 0xd0, 0xc9, 0x9c, 0x7c, 0x06, |
|
| 4196 |
- 0x96, 0x1f, 0xb3, 0xa1, 0xa9, 0x32, 0x85, 0x37, 0xd8, 0x71, 0xda, 0xbb, 0x46, 0x22, 0x5b, 0x95, |
|
| 4197 |
- 0x67, 0x4f, 0xd6, 0x2d, 0x65, 0xa0, 0x9a, 0xd6, 0xf8, 0x15, 0x3b, 0x7f, 0x27, 0x48, 0x13, 0x69, |
|
| 4198 |
- 0x8a, 0xc4, 0x2b, 0x8b, 0xcb, 0x37, 0x70, 0x86, 0xe9, 0xef, 0x1d, 0x16, 0xaa, 0x17, 0xa7, 0x0b, |
|
| 4199 |
- 0xa4, 0x89, 0xcd, 0x95, 0x42, 0x77, 0x13, 0x70, 0x56, 0x4c, 0xb7, 0xca, 0xca, 0x67, 0xad, 0x44, |
|
| 4200 |
- 0x57, 0xd9, 0x7f, 0x56, 0xb0, 0xb9, 0xae, 0x44, 0xc2, 0x3d, 0xc4, 0x5a, 0x9b, 0x3d, 0x52, 0xf3, |
|
| 4201 |
- 0x7d, 0x50, 0xf8, 0xe5, 0x78, 0x27, 0x0f, 0xcc, 0x22, 0x6e, 0x4e, 0x3b, 0xeb, 0x03, 0x7b, 0xbd, |
|
| 4202 |
- 0x25, 0xd8, 0x83, 0x71, 0xb1, 0x2f, 0xd4, 0x2f, 0xc5, 0xf5, 0x19, 0x17, 0x9a, 0x41, 0xbe, 0x02, |
|
| 4203 |
- 0xf0, 0xfc, 0x24, 0x66, 0x12, 0xdd, 0x09, 0x93, 0x87, 0xc2, 0x2b, 0x76, 0x27, 0xa8, 0x19, 0x2f, |
|
| 4204 |
- 0x39, 0x36, 0xb9, 0x8d, 0x0d, 0x90, 0x8d, 0x95, 0x54, 0x3e, 0xb9, 0x3e, 0x75, 0xda, 0xc6, 0xc5, |
|
| 4205 |
- 0xaa, 0x72, 0x81, 0x39, 0xad, 0x8c, 0x2d, 0xb4, 0xe2, 0x32, 0xa3, 0xac, 0xdb, 0xb0, 0xa2, 0x3e, |
|
| 4206 |
- 0xa6, 0xfa, 0x1e, 0x7f, 0xc0, 0xd2, 0x40, 0x26, 0xba, 0x94, 0x9e, 0xf0, 0xd1, 0xa0, 0x5a, 0x70, |
|
| 4207 |
- 0xd7, 0xe0, 0xcc, 0xb9, 0xaa, 0x32, 0x6f, 0xbb, 0xf4, 0xf8, 0xe9, 0xda, 0xdc, 0x9f, 0xf8, 0xfb, |
|
| 4208 |
- 0xfb, 0xe9, 0x5a, 0xe9, 0xbb, 0x67, 0x6b, 0xa5, 0xc7, 0xf8, 0xfb, 0x1d, 0x7f, 0x7f, 0xe1, 0xef, |
|
| 4209 |
- 0xa0, 0xac, 0xff, 0x58, 0x7c, 0xf8, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x10, 0x79, 0x5b, |
|
| 4210 |
- 0xb7, 0x0c, 0x00, 0x00, |
|
| 4129 |
+ 0x14, 0x24, 0x1c, 0x30, 0xa8, 0x7f, 0xf8, 0x23, 0x70, 0x6c, 0x93, 0x86, 0x92, 0xd4, 0x9a, 0xb4, |
|
| 4130 |
+ 0x95, 0x38, 0x59, 0x93, 0xf5, 0xd4, 0x59, 0x65, 0xbd, 0xb3, 0xcc, 0xce, 0xba, 0xca, 0x8d, 0x63, |
|
| 4131 |
+ 0xc5, 0x81, 0x1b, 0x47, 0x4e, 0x48, 0x1c, 0x39, 0xf2, 0x19, 0x7a, 0xe4, 0x82, 0xc4, 0xa9, 0xa2, |
|
| 4132 |
+ 0xfd, 0x04, 0x48, 0x7c, 0x01, 0xde, 0xcc, 0x8e, 0xed, 0x35, 0xdd, 0xb4, 0x1c, 0x7a, 0xb0, 0x34, |
|
| 4133 |
+ 0x7f, 0x7e, 0xbf, 0xf7, 0x66, 0xdf, 0xfb, 0xcd, 0x7b, 0x63, 0x70, 0xe3, 0x88, 0x7b, 0x71, 0x3d, |
|
| 4134 |
+ 0x92, 0x42, 0x09, 0x42, 0xfa, 0xc2, 0x3b, 0xe2, 0xb2, 0x1e, 0x3f, 0x64, 0x72, 0x78, 0xe4, 0xab, |
|
| 4135 |
+ 0xfa, 0xe8, 0x83, 0xaa, 0xab, 0x8e, 0x23, 0x6e, 0x01, 0xd5, 0xb3, 0x03, 0x31, 0x10, 0x66, 0xb8, |
|
| 4136 |
+ 0xa9, 0x47, 0x76, 0xf5, 0x7c, 0x3f, 0x91, 0x4c, 0xf9, 0x22, 0xdc, 0x1c, 0x0f, 0xd2, 0x8d, 0xda, |
|
| 4137 |
+ 0x0f, 0x0e, 0x94, 0xf6, 0x44, 0x9f, 0xef, 0xa3, 0x0f, 0xb2, 0x0d, 0x2e, 0x0b, 0x43, 0xa1, 0x0c, |
|
| 4138 |
+ 0x20, 0xae, 0x14, 0x36, 0x0a, 0x57, 0xdd, 0xc6, 0x7a, 0xfd, 0x79, 0x97, 0xf5, 0xe6, 0x14, 0xb6, |
|
| 4139 |
+ 0xe5, 0x3c, 0x7e, 0xb2, 0x3e, 0x47, 0xb3, 0x4c, 0xf2, 0x3e, 0x38, 0x52, 0x04, 0xbc, 0x32, 0x8f, |
|
| 4140 |
+ 0x16, 0x4e, 0x35, 0x2e, 0xe5, 0x59, 0xd0, 0x4e, 0x29, 0x62, 0xa8, 0x41, 0xa2, 0x6b, 0x18, 0xf2, |
|
| 4141 |
+ 0xe1, 0x01, 0x97, 0xf1, 0xa1, 0x1f, 0x55, 0x16, 0x0c, 0xef, 0xed, 0x93, 0x78, 0xfa, 0xb0, 0xf5, |
|
| 4142 |
+ 0xdd, 0x09, 0x9c, 0x66, 0xa8, 0x64, 0x17, 0xca, 0x6c, 0xc4, 0xfc, 0x80, 0x1d, 0xf8, 0x81, 0xaf, |
|
| 4143 |
+ 0x8e, 0x2b, 0x8e, 0x31, 0xf5, 0xce, 0x0b, 0x4d, 0x35, 0x33, 0x04, 0x3a, 0x43, 0xaf, 0xf5, 0x01, |
|
| 4144 |
+ 0xa6, 0x8e, 0xc8, 0x5b, 0xb0, 0xd4, 0xed, 0xec, 0xb5, 0x77, 0xf6, 0xb6, 0x57, 0xe7, 0xaa, 0x17, |
|
| 4145 |
+ 0xbe, 0xff, 0x69, 0xe3, 0x35, 0x6d, 0x63, 0x0a, 0xe8, 0xf2, 0xb0, 0xef, 0x87, 0x03, 0x72, 0x15, |
|
| 4146 |
+ 0x4a, 0xcd, 0x56, 0xab, 0xd3, 0xbd, 0xdb, 0x69, 0xaf, 0x16, 0xaa, 0x55, 0x04, 0x9e, 0x9b, 0x05, |
|
| 4147 |
+ 0x36, 0x3d, 0x8f, 0x47, 0x8a, 0xf7, 0xab, 0xce, 0xa3, 0x9f, 0xd7, 0xe6, 0x6a, 0x8f, 0x0a, 0x50, |
|
| 4148 |
+ 0xce, 0x1e, 0x02, 0x1d, 0x15, 0x9b, 0xad, 0xbb, 0x3b, 0xf7, 0x3b, 0xe8, 0x67, 0x42, 0xcf, 0x22, |
|
| 4149 |
+ 0x9a, 0x9e, 0xf2, 0x47, 0x9c, 0x5c, 0x81, 0xc5, 0x6e, 0xf3, 0xde, 0x7e, 0x07, 0xbd, 0x4c, 0x8e, |
|
| 4150 |
+ 0x93, 0x85, 0x75, 0x59, 0x12, 0x1b, 0x54, 0x9b, 0x36, 0x77, 0xf6, 0x56, 0xe7, 0xf3, 0x51, 0x6d, |
|
| 4151 |
+ 0xc9, 0xfc, 0xd0, 0x1e, 0xe5, 0x37, 0x07, 0xdc, 0x7d, 0x2e, 0x47, 0xbe, 0xf7, 0x8a, 0x35, 0x71, |
|
| 4152 |
+ 0x0d, 0x1c, 0xc5, 0xe2, 0x23, 0xa3, 0x09, 0x37, 0x5f, 0x13, 0x77, 0x71, 0x5f, 0x3b, 0xb5, 0x74, |
|
| 4153 |
+ 0x83, 0xd7, 0xca, 0x90, 0x3c, 0x0a, 0x7c, 0x8f, 0x61, 0xbc, 0x8c, 0x32, 0xdc, 0xc6, 0x9b, 0x79, |
|
| 4154 |
+ 0x6c, 0x3a, 0x41, 0xd9, 0xf3, 0xdf, 0x9a, 0xa3, 0x19, 0x2a, 0xf9, 0x04, 0x8a, 0x83, 0x40, 0x1c, |
|
| 4155 |
+ 0xb0, 0xc0, 0x68, 0xc2, 0x6d, 0x5c, 0xce, 0x33, 0xb2, 0x6d, 0x10, 0x53, 0x03, 0x96, 0x42, 0x6e, |
|
| 4156 |
+ 0x40, 0x31, 0x89, 0xfa, 0x68, 0xa7, 0x52, 0x34, 0xe4, 0x8d, 0x3c, 0xf2, 0x3d, 0x83, 0x68, 0x89, |
|
| 4157 |
+ 0xf0, 0x81, 0x3f, 0xa0, 0x16, 0x4f, 0xf6, 0xa1, 0x14, 0x72, 0xf5, 0x50, 0xc8, 0xa3, 0xb8, 0xb2, |
|
| 4158 |
+ 0xb4, 0xb1, 0x80, 0xdc, 0xeb, 0x79, 0xdc, 0x4c, 0xcc, 0xeb, 0x7b, 0x29, 0xbe, 0xa9, 0x14, 0xf3, |
|
| 4159 |
+ 0x0e, 0x87, 0x3c, 0x54, 0xd6, 0xe4, 0xc4, 0x10, 0xf9, 0x14, 0x4a, 0x28, 0xb5, 0x48, 0xf8, 0xa1, |
|
| 4160 |
+ 0xaa, 0x94, 0x4e, 0x3e, 0x50, 0xc7, 0x62, 0xb4, 0x55, 0x3a, 0x61, 0x54, 0x6f, 0xc3, 0xf9, 0x13, |
|
| 4161 |
+ 0x5c, 0x90, 0x73, 0x50, 0x54, 0x4c, 0x0e, 0xb8, 0x32, 0x99, 0x5e, 0xa6, 0x76, 0x46, 0x2a, 0xb0, |
|
| 4162 |
+ 0xc4, 0x02, 0x9f, 0xc5, 0x3c, 0xc6, 0x04, 0x2e, 0xe0, 0xc6, 0x78, 0xba, 0x55, 0x04, 0x67, 0x88, |
|
| 4163 |
+ 0x7a, 0xaa, 0x6d, 0xc2, 0x99, 0xe7, 0x32, 0x40, 0xaa, 0x50, 0xb2, 0x19, 0x48, 0xa5, 0xe3, 0xd0, |
|
| 4164 |
+ 0xc9, 0xbc, 0x76, 0x1a, 0x56, 0x66, 0xa2, 0x5d, 0xfb, 0x63, 0x1e, 0x4a, 0x63, 0x09, 0x90, 0x26, |
|
| 4165 |
+ 0x2c, 0x7b, 0x22, 0x54, 0x28, 0x4c, 0x2e, 0xad, 0xea, 0x72, 0x13, 0xd6, 0x1a, 0x83, 0x34, 0x0b, |
|
| 4166 |
+ 0x13, 0x36, 0x65, 0x91, 0x2f, 0x61, 0x59, 0xf2, 0x58, 0x24, 0xd2, 0x33, 0xa7, 0xd6, 0x26, 0xae, |
|
| 4167 |
+ 0xe6, 0x0b, 0x27, 0x05, 0x51, 0xfe, 0x6d, 0xe2, 0x4b, 0xae, 0xa3, 0x11, 0xd3, 0x29, 0x15, 0x85, |
|
| 4168 |
+ 0xb3, 0x84, 0x13, 0x0c, 0x84, 0x7a, 0x91, 0x72, 0x68, 0x0a, 0xe9, 0x0a, 0xfc, 0xba, 0x63, 0x3a, |
|
| 4169 |
+ 0x66, 0x20, 0x79, 0x39, 0x0a, 0x98, 0x67, 0xac, 0x56, 0x16, 0x0d, 0xfd, 0xf5, 0x3c, 0x7a, 0x77, |
|
| 4170 |
+ 0x0c, 0xa2, 0x53, 0x3c, 0xb9, 0x09, 0x10, 0x88, 0x41, 0xaf, 0x2f, 0xf1, 0xae, 0x4b, 0xab, 0xbc, |
|
| 4171 |
+ 0x6a, 0x1e, 0xbb, 0x6d, 0x10, 0x74, 0x19, 0xd1, 0xe9, 0x70, 0x6b, 0x19, 0x0f, 0x9d, 0x84, 0xca, |
|
| 4172 |
+ 0x1f, 0xf2, 0xda, 0xaf, 0x0e, 0xac, 0xcc, 0x84, 0x89, 0x9c, 0x85, 0x45, 0x7f, 0xc8, 0x06, 0xdc, |
|
| 4173 |
+ 0x26, 0x39, 0x9d, 0x90, 0x0e, 0x14, 0xb1, 0x22, 0xf0, 0x20, 0x4d, 0xb1, 0xdb, 0x78, 0xef, 0xa5, |
|
| 4174 |
+ 0xf1, 0xae, 0x7f, 0x6d, 0xf0, 0x9d, 0x50, 0xc9, 0x63, 0x6a, 0xc9, 0x5a, 0x2a, 0x9e, 0x18, 0x0e, |
|
| 4175 |
+ 0x59, 0xa8, 0x6f, 0xab, 0x91, 0x8a, 0x9d, 0x12, 0x02, 0x0e, 0xaa, 0x29, 0xc6, 0x28, 0xea, 0x65, |
|
| 4176 |
+ 0x33, 0x26, 0xab, 0xb0, 0xc0, 0xc3, 0x11, 0x46, 0x46, 0x2f, 0xe9, 0xa1, 0x5e, 0xe9, 0xfb, 0xe9, |
|
| 4177 |
+ 0xd7, 0xe2, 0x0a, 0x0e, 0x35, 0x0f, 0xcb, 0x98, 0xc4, 0xeb, 0xa3, 0x97, 0xcc, 0x98, 0x5c, 0x87, |
|
| 4178 |
+ 0xe2, 0x50, 0xe0, 0x07, 0xc6, 0xa8, 0x7f, 0x7d, 0xd8, 0x0b, 0x79, 0x87, 0xdd, 0xd5, 0x08, 0x5b, |
|
| 4179 |
+ 0x4d, 0x2c, 0x9c, 0xdc, 0x82, 0x33, 0xb1, 0x12, 0x51, 0x6f, 0x20, 0x31, 0xca, 0xbd, 0x88, 0x4b, |
|
| 4180 |
+ 0x5f, 0xf4, 0x2b, 0xcb, 0x27, 0x17, 0xa5, 0xb6, 0x6d, 0x98, 0xf4, 0xb4, 0xa6, 0x6d, 0x6b, 0x56, |
|
| 4181 |
+ 0xd7, 0x90, 0x48, 0x17, 0xca, 0x51, 0x12, 0x04, 0x3d, 0x11, 0xa5, 0xb5, 0x11, 0x8c, 0x91, 0xff, |
|
| 4182 |
+ 0x11, 0xb5, 0x2e, 0xb2, 0xee, 0xa4, 0x24, 0xea, 0x46, 0xd3, 0x89, 0xbe, 0x7d, 0x03, 0x29, 0x92, |
|
| 4183 |
+ 0x28, 0xae, 0xb8, 0x26, 0x1e, 0x76, 0x56, 0xbd, 0x09, 0x6e, 0x26, 0xd2, 0x3a, 0x42, 0x47, 0xfc, |
|
| 4184 |
+ 0xd8, 0x26, 0x4f, 0x0f, 0x75, 0x42, 0x47, 0x2c, 0x48, 0xd2, 0x8e, 0x8b, 0x09, 0x35, 0x93, 0x8f, |
|
| 4185 |
+ 0xe7, 0x6f, 0x14, 0xaa, 0x0d, 0x70, 0x33, 0xee, 0xc8, 0x1b, 0xb0, 0x22, 0xf9, 0xc0, 0x8f, 0xd1, |
|
| 4186 |
+ 0x4c, 0x8f, 0x25, 0xea, 0xb0, 0xf2, 0x85, 0x21, 0x94, 0xc7, 0x8b, 0x4d, 0x5c, 0xab, 0xfd, 0x83, |
|
| 4187 |
+ 0xed, 0x28, 0x5b, 0x3a, 0x48, 0x2b, 0xbd, 0xe3, 0xc6, 0xe3, 0xa9, 0xc6, 0xe6, 0xcb, 0x4a, 0x8d, |
|
| 4188 |
+ 0xb9, 0x51, 0x41, 0xa2, 0x3d, 0xee, 0xea, 0x36, 0x6f, 0xc8, 0xe4, 0x23, 0x58, 0x8c, 0x84, 0x54, |
|
| 4189 |
+ 0x63, 0x75, 0xad, 0xe5, 0xde, 0x02, 0x04, 0xd8, 0x62, 0x97, 0x82, 0x6b, 0x87, 0x70, 0x6a, 0xd6, |
|
| 4190 |
+ 0x1a, 0x76, 0xb3, 0x85, 0xfb, 0x3b, 0x5d, 0x6c, 0x8c, 0x17, 0xb1, 0x97, 0x9d, 0x9f, 0xdd, 0xbc, |
|
| 4191 |
+ 0xef, 0x4b, 0x95, 0xb0, 0x60, 0xa7, 0x4b, 0xde, 0xc5, 0x9e, 0xb7, 0xb7, 0x4f, 0x29, 0x76, 0xc6, |
|
| 4192 |
+ 0x75, 0xc4, 0x5d, 0x9c, 0xc5, 0xe9, 0x2d, 0x94, 0x43, 0x9f, 0x8a, 0x83, 0x49, 0xe7, 0xfb, 0x71, |
|
| 4193 |
+ 0x1e, 0x5c, 0x5b, 0x16, 0x5f, 0x6d, 0xe7, 0xfb, 0x1c, 0x56, 0xd2, 0x1b, 0xdc, 0xf3, 0xcc, 0xa7, |
|
| 4194 |
+ 0xd9, 0x5a, 0xf4, 0xa2, 0x8b, 0x5c, 0x4e, 0x09, 0xb6, 0x28, 0x5f, 0x86, 0xb2, 0x1f, 0x8d, 0xae, |
|
| 4195 |
+ 0xf5, 0x78, 0xc8, 0x0e, 0x02, 0xdb, 0x04, 0x4b, 0xd4, 0xd5, 0x6b, 0x9d, 0x74, 0x49, 0x17, 0x5a, |
|
| 4196 |
+ 0x0c, 0x3e, 0x97, 0xa1, 0x6d, 0x6f, 0x25, 0x3a, 0x99, 0x93, 0xcf, 0xc0, 0xf1, 0x23, 0x36, 0xb4, |
|
| 4197 |
+ 0xd5, 0x27, 0xf7, 0x0b, 0x76, 0xba, 0xcd, 0x5d, 0x2b, 0x91, 0xad, 0xd2, 0xb3, 0x27, 0xeb, 0x8e, |
|
| 4198 |
+ 0x5e, 0xa0, 0x86, 0x56, 0xfb, 0x05, 0x5f, 0x04, 0xad, 0x20, 0x89, 0x95, 0x2d, 0x1e, 0xaf, 0x2c, |
|
| 4199 |
+ 0x2e, 0xdf, 0xc0, 0x19, 0x66, 0xde, 0x41, 0x2c, 0xd4, 0x37, 0xd1, 0x14, 0x4e, 0x1b, 0x9b, 0x2b, |
|
| 4200 |
+ 0xb9, 0xe6, 0x26, 0xe0, 0xb4, 0xc8, 0x6e, 0x15, 0xb5, 0xcd, 0x4a, 0x81, 0xae, 0xb2, 0xff, 0xec, |
|
| 4201 |
+ 0x60, 0xd3, 0x5d, 0x11, 0xd2, 0x3b, 0xc4, 0x1a, 0x9c, 0x5e, 0x5e, 0xfb, 0x6e, 0xc8, 0x7d, 0x51, |
|
| 4202 |
+ 0xde, 0xc9, 0x02, 0xd3, 0x88, 0xdb, 0xd3, 0xce, 0xda, 0xc0, 0x37, 0x80, 0x23, 0xd9, 0x83, 0x71, |
|
| 4203 |
+ 0x13, 0xc8, 0xd5, 0x2f, 0xc5, 0xfd, 0x19, 0x13, 0x86, 0x41, 0xbe, 0x02, 0xe8, 0xfb, 0x71, 0xc4, |
|
| 4204 |
+ 0x14, 0x9a, 0x93, 0x36, 0x0f, 0xb9, 0x9f, 0xd8, 0x9e, 0xa0, 0x66, 0xac, 0x64, 0xd8, 0xe4, 0x36, |
|
| 4205 |
+ 0x36, 0x46, 0x36, 0x56, 0x52, 0xf1, 0xe4, 0xba, 0xd5, 0x6a, 0x5a, 0x13, 0xab, 0xda, 0x04, 0xe6, |
|
| 4206 |
+ 0xb4, 0x34, 0x5e, 0xa1, 0x25, 0x8f, 0x59, 0x65, 0xdd, 0x86, 0x15, 0xfd, 0xc8, 0xea, 0xf5, 0xf9, |
|
| 4207 |
+ 0x03, 0x96, 0x04, 0x2a, 0x36, 0x25, 0xf6, 0x84, 0xc7, 0x84, 0x6e, 0xcd, 0x6d, 0x8b, 0xb3, 0xe7, |
|
| 4208 |
+ 0x2a, 0xab, 0xec, 0xda, 0xa5, 0xc7, 0x4f, 0xd7, 0xe6, 0xfe, 0xc4, 0xdf, 0xdf, 0x4f, 0xd7, 0x0a, |
|
| 4209 |
+ 0xdf, 0x3d, 0x5b, 0x2b, 0x3c, 0xc6, 0xdf, 0xef, 0xf8, 0xfb, 0x0b, 0x7f, 0x07, 0x45, 0xf3, 0x87, |
|
| 4210 |
+ 0xe3, 0xc3, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xa4, 0x46, 0x7c, 0xf6, 0xcf, 0x0c, 0x00, 0x00, |
|
| 4211 | 4211 |
} |
| ... | ... |
@@ -160,8 +160,15 @@ message ContainerSpec {
|
| 160 | 160 |
string dir = 6; |
| 161 | 161 |
|
| 162 | 162 |
// User specifies the user that should be employed to run the container. |
| 163 |
+ // |
|
| 164 |
+ // Note that the primary group may be specified by appending the group name |
|
| 165 |
+ // or id to the user name, separated by a `:`. This syntax is |
|
| 166 |
+ // `<user>:<group>`. |
|
| 163 | 167 |
string user = 7; |
| 164 | 168 |
|
| 169 |
+ // Groups specifies supplementary groups available to the user. |
|
| 170 |
+ repeated string groups = 11; |
|
| 171 |
+ |
|
| 165 | 172 |
repeated Mount mounts = 8 [(gogoproto.nullable) = false]; |
| 166 | 173 |
|
| 167 | 174 |
// StopGracePeriod the grace period for stopping the container before |
| ... | ... |
@@ -57,19 +57,17 @@ const ( |
| 57 | 57 |
RootCAExpiration = "630720000s" |
| 58 | 58 |
// DefaultNodeCertExpiration represents the default expiration for node certificates (3 months) |
| 59 | 59 |
DefaultNodeCertExpiration = 2160 * time.Hour |
| 60 |
+ // CertBackdate represents the amount of time each certificate is backdated to try to avoid |
|
| 61 |
+ // clock drift issues. |
|
| 62 |
+ CertBackdate = 1 * time.Hour |
|
| 60 | 63 |
// CertLowerRotationRange represents the minimum fraction of time that we will wait when randomly |
| 61 | 64 |
// choosing our next certificate rotation |
| 62 | 65 |
CertLowerRotationRange = 0.5 |
| 63 | 66 |
// CertUpperRotationRange represents the maximum fraction of time that we will wait when randomly |
| 64 | 67 |
// choosing our next certificate rotation |
| 65 | 68 |
CertUpperRotationRange = 0.8 |
| 66 |
- // MinNodeCertExpiration represents the minimum expiration for node certificates (25 + 5 minutes) |
|
| 67 |
- // X - 5 > CertUpperRotationRange * X <=> X < 5/(1 - CertUpperRotationRange) |
|
| 68 |
- // Since we're issuing certificates 5 minutes in the past to get around clock drifts, and |
|
| 69 |
- // we're selecting a random rotation distribution range from CertLowerRotationRange to |
|
| 70 |
- // CertUpperRotationRange, we need to ensure that we don't accept an expiration time that will |
|
| 71 |
- // make a node able to randomly choose the next rotation after the expiration of the certificate. |
|
| 72 |
- MinNodeCertExpiration = 30 * time.Minute |
|
| 69 |
+ // MinNodeCertExpiration represents the minimum expiration for node certificates |
|
| 70 |
+ MinNodeCertExpiration = 1 * time.Hour |
|
| 73 | 71 |
) |
| 74 | 72 |
|
| 75 | 73 |
// ErrNoLocalRootCA is an error type used to indicate that the local root CA |
| ... | ... |
@@ -109,12 +109,6 @@ func (s *SecurityConfig) UpdateRootCA(cert, key []byte, certExpiry time.Duration |
| 109 | 109 |
return err |
| 110 | 110 |
} |
| 111 | 111 |
|
| 112 |
-// DefaultPolicy is the default policy used by the signers to ensure that the only fields |
|
| 113 |
-// from the remote CSRs we trust are: PublicKey, PublicKeyAlgorithm and SignatureAlgorithm. |
|
| 114 |
-func DefaultPolicy() *cfconfig.Signing {
|
|
| 115 |
- return SigningPolicy(DefaultNodeCertExpiration) |
|
| 116 |
-} |
|
| 117 |
- |
|
| 118 | 112 |
// SigningPolicy creates a policy used by the signer to ensure that the only fields |
| 119 | 113 |
// from the remote CSRs we trust are: PublicKey, PublicKeyAlgorithm and SignatureAlgorithm. |
| 120 | 114 |
// It receives the duration a certificate will be valid for |
| ... | ... |
@@ -124,10 +118,14 @@ func SigningPolicy(certExpiry time.Duration) *cfconfig.Signing {
|
| 124 | 124 |
certExpiry = DefaultNodeCertExpiration |
| 125 | 125 |
} |
| 126 | 126 |
|
| 127 |
+ // Add the backdate |
|
| 128 |
+ certExpiry = certExpiry + CertBackdate |
|
| 129 |
+ |
|
| 127 | 130 |
return &cfconfig.Signing{
|
| 128 | 131 |
Default: &cfconfig.SigningProfile{
|
| 129 |
- Usage: []string{"signing", "key encipherment", "server auth", "client auth"},
|
|
| 130 |
- Expiry: certExpiry, |
|
| 132 |
+ Usage: []string{"signing", "key encipherment", "server auth", "client auth"},
|
|
| 133 |
+ Expiry: certExpiry, |
|
| 134 |
+ Backdate: CertBackdate, |
|
| 131 | 135 |
// Only trust the key components from the CSR. Everything else should |
| 132 | 136 |
// come directly from API call params. |
| 133 | 137 |
CSRWhitelist: &cfconfig.CSRWhitelist{
|
| ... | ... |
@@ -396,7 +394,7 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, baseCertDir string, |
| 396 | 396 |
// calculateRandomExpiry returns a random duration between 50% and 80% of the original |
| 397 | 397 |
// duration |
| 398 | 398 |
func calculateRandomExpiry(expiresIn time.Duration) time.Duration {
|
| 399 |
- if expiresIn.Minutes() < 1 {
|
|
| 399 |
+ if expiresIn.Minutes() <= 1 {
|
|
| 400 | 400 |
return time.Second |
| 401 | 401 |
} |
| 402 | 402 |
|
| ... | ... |
@@ -306,8 +306,7 @@ func (s *Server) Run(ctx context.Context) error {
|
| 306 | 306 |
s.mu.Unlock() |
| 307 | 307 |
|
| 308 | 308 |
defer s.wg.Done() |
| 309 |
- logger := log.G(ctx).WithField("module", "ca")
|
|
| 310 |
- ctx = log.WithLogger(ctx, logger) |
|
| 309 |
+ ctx = log.WithModule(ctx, "ca") |
|
| 311 | 310 |
|
| 312 | 311 |
// Retrieve the channels to keep track of changes in the cluster |
| 313 | 312 |
// Retrieve all the currently registered nodes |
| ... | ... |
@@ -1,6 +1,8 @@ |
| 1 | 1 |
package log |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "path" |
|
| 5 |
+ |
|
| 4 | 6 |
"github.com/Sirupsen/logrus" |
| 5 | 7 |
"golang.org/x/net/context" |
| 6 | 8 |
) |
| ... | ... |
@@ -16,7 +18,10 @@ var ( |
| 16 | 16 |
L = logrus.NewEntry(logrus.StandardLogger()) |
| 17 | 17 |
) |
| 18 | 18 |
|
| 19 |
-type loggerKey struct{}
|
|
| 19 |
+type ( |
|
| 20 |
+ loggerKey struct{}
|
|
| 21 |
+ moduleKey struct{}
|
|
| 22 |
+) |
|
| 20 | 23 |
|
| 21 | 24 |
// WithLogger returns a new context with the provided logger. Use in |
| 22 | 25 |
// combination with logger.WithField(s) for great effect. |
| ... | ... |
@@ -35,3 +40,42 @@ func GetLogger(ctx context.Context) *logrus.Entry {
|
| 35 | 35 |
|
| 36 | 36 |
return logger.(*logrus.Entry) |
| 37 | 37 |
} |
| 38 |
+ |
|
| 39 |
+// WithModule adds the module to the context, appending it with a slash if a |
|
| 40 |
+// module already exists. A module is just an roughly correlated defined by the |
|
| 41 |
+// call tree for a given context. |
|
| 42 |
+// |
|
| 43 |
+// As an example, we might have a "node" module already part of a context. If |
|
| 44 |
+// this function is called with "tls", the new value of module will be |
|
| 45 |
+// "node/tls". |
|
| 46 |
+// |
|
| 47 |
+// Modules represent the call path. If the new module and last module are the |
|
| 48 |
+// same, a new module entry will not be created. If the new module and old |
|
| 49 |
+// older module are the same but separated by other modules, the cycle will be |
|
| 50 |
+// represented by the module path. |
|
| 51 |
+func WithModule(ctx context.Context, module string) context.Context {
|
|
| 52 |
+ parent := GetModulePath(ctx) |
|
| 53 |
+ |
|
| 54 |
+ if parent != "" {
|
|
| 55 |
+ // don't re-append module when module is the same. |
|
| 56 |
+ if path.Base(parent) == module {
|
|
| 57 |
+ return ctx |
|
| 58 |
+ } |
|
| 59 |
+ |
|
| 60 |
+ module = path.Join(parent, module) |
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 63 |
+ ctx = WithLogger(ctx, GetLogger(ctx).WithField("module", module))
|
|
| 64 |
+ return context.WithValue(ctx, moduleKey{}, module)
|
|
| 65 |
+} |
|
| 66 |
+ |
|
| 67 |
+// GetModulePath returns the module path for the provided context. If no module |
|
| 68 |
+// is set, an empty string is returned. |
|
| 69 |
+func GetModulePath(ctx context.Context) string {
|
|
| 70 |
+ module := ctx.Value(moduleKey{})
|
|
| 71 |
+ if module == nil {
|
|
| 72 |
+ return "" |
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 75 |
+ return module.(string) |
|
| 76 |
+} |
| ... | ... |
@@ -9,10 +9,10 @@ |
| 9 | 9 |
// allocation, they all have to agree on that. The way this achieved |
| 10 | 10 |
// in `allocator` is by creating a `taskBallot` to which all task |
| 11 | 11 |
// allocators register themselves as mandatory voters. For each task |
| 12 |
-// that needs allocation, each allocator indepdently votes to indicate |
|
| 12 |
+// that needs allocation, each allocator independently votes to indicate |
|
| 13 | 13 |
// the completion of their allocation. Once all registered voters have |
| 14 | 14 |
// voted then the task is moved to ALLOCATED state. |
| 15 | 15 |
// |
| 16 | 16 |
// Other than the coordination needed for task ALLOCATED state, all |
| 17 |
-// the allocators function fairly indepdently. |
|
| 17 |
+// the allocators function fairly independently. |
|
| 18 | 18 |
package allocator |
| ... | ... |
@@ -193,7 +193,7 @@ func (a *Allocator) doNetworkInit(ctx context.Context) error {
|
| 193 | 193 |
} |
| 194 | 194 |
|
| 195 | 195 |
for _, s := range services {
|
| 196 |
- if !serviceAllocationNeeded(s, nc) {
|
|
| 196 |
+ if nc.nwkAllocator.IsServiceAllocated(s) {
|
|
| 197 | 197 |
continue |
| 198 | 198 |
} |
| 199 | 199 |
|
| ... | ... |
@@ -304,7 +304,7 @@ func (a *Allocator) doNetworkAlloc(ctx context.Context, ev events.Event) {
|
| 304 | 304 |
case state.EventCreateService: |
| 305 | 305 |
s := v.Service.Copy() |
| 306 | 306 |
|
| 307 |
- if !serviceAllocationNeeded(s, nc) {
|
|
| 307 |
+ if nc.nwkAllocator.IsServiceAllocated(s) {
|
|
| 308 | 308 |
break |
| 309 | 309 |
} |
| 310 | 310 |
|
| ... | ... |
@@ -315,7 +315,7 @@ func (a *Allocator) doNetworkAlloc(ctx context.Context, ev events.Event) {
|
| 315 | 315 |
case state.EventUpdateService: |
| 316 | 316 |
s := v.Service.Copy() |
| 317 | 317 |
|
| 318 |
- if !serviceAllocationNeeded(s, nc) {
|
|
| 318 |
+ if nc.nwkAllocator.IsServiceAllocated(s) {
|
|
| 319 | 319 |
break |
| 320 | 320 |
} |
| 321 | 321 |
|
| ... | ... |
@@ -326,13 +326,13 @@ func (a *Allocator) doNetworkAlloc(ctx context.Context, ev events.Event) {
|
| 326 | 326 |
case state.EventDeleteService: |
| 327 | 327 |
s := v.Service.Copy() |
| 328 | 328 |
|
| 329 |
- if serviceAllocationNeeded(s, nc) {
|
|
| 330 |
- break |
|
| 331 |
- } |
|
| 332 |
- |
|
| 333 | 329 |
if err := nc.nwkAllocator.ServiceDeallocate(s); err != nil {
|
| 334 | 330 |
log.G(ctx).Errorf("Failed deallocation during delete of service %s: %v", s.ID, err)
|
| 335 | 331 |
} |
| 332 |
+ |
|
| 333 |
+ // Remove it from unallocatedServices just in case |
|
| 334 |
+ // it's still there. |
|
| 335 |
+ delete(nc.unallocatedServices, s.ID) |
|
| 336 | 336 |
case state.EventCreateNode, state.EventUpdateNode, state.EventDeleteNode: |
| 337 | 337 |
a.doNodeAlloc(ctx, nc, ev) |
| 338 | 338 |
case state.EventCreateTask, state.EventUpdateTask, state.EventDeleteTask: |
| ... | ... |
@@ -382,23 +382,6 @@ func (a *Allocator) doNodeAlloc(ctx context.Context, nc *networkContext, ev even |
| 382 | 382 |
} |
| 383 | 383 |
} |
| 384 | 384 |
|
| 385 |
-// serviceAllocationNeeded returns if a service needs allocation or not. |
|
| 386 |
-func serviceAllocationNeeded(s *api.Service, nc *networkContext) bool {
|
|
| 387 |
- // Service needs allocation if: |
|
| 388 |
- // Spec has network attachments and endpoint resolution mode is VIP OR |
|
| 389 |
- // Spec has non-zero number of exposed ports and ingress routing is SwarmPort |
|
| 390 |
- if (len(s.Spec.Networks) != 0 && |
|
| 391 |
- (s.Spec.Endpoint == nil || |
|
| 392 |
- (s.Spec.Endpoint != nil && |
|
| 393 |
- s.Spec.Endpoint.Mode == api.ResolutionModeVirtualIP))) || |
|
| 394 |
- (s.Spec.Endpoint != nil && |
|
| 395 |
- len(s.Spec.Endpoint.Ports) != 0) {
|
|
| 396 |
- return !nc.nwkAllocator.IsServiceAllocated(s) |
|
| 397 |
- } |
|
| 398 |
- |
|
| 399 |
- return false |
|
| 400 |
-} |
|
| 401 |
- |
|
| 402 | 385 |
// taskRunning checks whether a task is either actively running, or in the |
| 403 | 386 |
// process of starting up. |
| 404 | 387 |
func taskRunning(t *api.Task) bool {
|
| ... | ... |
@@ -420,7 +403,7 @@ func taskReadyForNetworkVote(t *api.Task, s *api.Service, nc *networkContext) bo |
| 420 | 420 |
// network configured or service endpoints have been |
| 421 | 421 |
// allocated. |
| 422 | 422 |
return (len(t.Networks) == 0 || nc.nwkAllocator.IsTaskAllocated(t)) && |
| 423 |
- (s == nil || !serviceAllocationNeeded(s, nc)) |
|
| 423 |
+ (s == nil || nc.nwkAllocator.IsServiceAllocated(s)) |
|
| 424 | 424 |
} |
| 425 | 425 |
|
| 426 | 426 |
func taskUpdateNetworks(t *api.Task, networks []*api.NetworkAttachment) {
|
| ... | ... |
@@ -599,6 +582,22 @@ func (a *Allocator) allocateService(ctx context.Context, nc *networkContext, s * |
| 599 | 599 |
return err |
| 600 | 600 |
} |
| 601 | 601 |
|
| 602 |
+ // If the service doesn't expose ports any more and if we have |
|
| 603 |
+ // any lingering virtual IP references for ingress network |
|
| 604 |
+ // clean them up here. |
|
| 605 |
+ if s.Spec.Endpoint == nil || len(s.Spec.Endpoint.Ports) == 0 {
|
|
| 606 |
+ if s.Endpoint != nil {
|
|
| 607 |
+ for i, vip := range s.Endpoint.VirtualIPs {
|
|
| 608 |
+ if vip.NetworkID == nc.ingressNetwork.ID {
|
|
| 609 |
+ n := len(s.Endpoint.VirtualIPs) |
|
| 610 |
+ s.Endpoint.VirtualIPs[i], s.Endpoint.VirtualIPs[n-1] = s.Endpoint.VirtualIPs[n-1], nil |
|
| 611 |
+ s.Endpoint.VirtualIPs = s.Endpoint.VirtualIPs[:n-1] |
|
| 612 |
+ break |
|
| 613 |
+ } |
|
| 614 |
+ } |
|
| 615 |
+ } |
|
| 616 |
+ } |
|
| 617 |
+ |
|
| 602 | 618 |
if err := a.store.Update(func(tx store.Tx) error {
|
| 603 | 619 |
for {
|
| 604 | 620 |
err := store.UpdateService(tx, s) |
| ... | ... |
@@ -670,7 +669,7 @@ func (a *Allocator) allocateTask(ctx context.Context, nc *networkContext, tx sto |
| 670 | 670 |
return nil, fmt.Errorf("could not find service %s", t.ServiceID)
|
| 671 | 671 |
} |
| 672 | 672 |
|
| 673 |
- if serviceAllocationNeeded(s, nc) {
|
|
| 673 |
+ if !nc.nwkAllocator.IsServiceAllocated(s) {
|
|
| 674 | 674 |
return nil, fmt.Errorf("service %s to which this task %s belongs has pending allocations", s.ID, t.ID)
|
| 675 | 675 |
} |
| 676 | 676 |
|
| ... | ... |
@@ -733,7 +732,7 @@ func (a *Allocator) procUnallocatedNetworks(ctx context.Context, nc *networkCont |
| 733 | 733 |
|
| 734 | 734 |
func (a *Allocator) procUnallocatedServices(ctx context.Context, nc *networkContext) {
|
| 735 | 735 |
for _, s := range nc.unallocatedServices {
|
| 736 |
- if serviceAllocationNeeded(s, nc) {
|
|
| 736 |
+ if !nc.nwkAllocator.IsServiceAllocated(s) {
|
|
| 737 | 737 |
if err := a.allocateService(ctx, nc, s); err != nil {
|
| 738 | 738 |
log.G(ctx).Debugf("Failed allocation of unallocated service %s: %v", s.ID, err)
|
| 739 | 739 |
continue |
| ... | ... |
@@ -165,15 +165,29 @@ func (na *NetworkAllocator) ServiceAllocate(s *api.Service) (err error) {
|
| 165 | 165 |
} |
| 166 | 166 |
}() |
| 167 | 167 |
|
| 168 |
- // If ResolutionMode is DNSRR do not try allocating VIPs. |
|
| 169 |
- if s.Spec.Endpoint != nil && s.Spec.Endpoint.Mode == api.ResolutionModeDNSRoundRobin {
|
|
| 170 |
- return |
|
| 168 |
+ if s.Endpoint == nil {
|
|
| 169 |
+ s.Endpoint = &api.Endpoint{}
|
|
| 171 | 170 |
} |
| 171 |
+ s.Endpoint.Spec = s.Spec.Endpoint.Copy() |
|
| 172 | 172 |
|
| 173 |
- if s.Endpoint == nil {
|
|
| 174 |
- s.Endpoint = &api.Endpoint{
|
|
| 175 |
- Spec: s.Spec.Endpoint.Copy(), |
|
| 173 |
+ // If ResolutionMode is DNSRR do not try allocating VIPs, but |
|
| 174 |
+ // free any VIP from previous state. |
|
| 175 |
+ if s.Spec.Endpoint != nil && s.Spec.Endpoint.Mode == api.ResolutionModeDNSRoundRobin {
|
|
| 176 |
+ if s.Endpoint != nil {
|
|
| 177 |
+ for _, vip := range s.Endpoint.VirtualIPs {
|
|
| 178 |
+ if err := na.deallocateVIP(vip); err != nil {
|
|
| 179 |
+ // don't bail here, deallocate as many as possible. |
|
| 180 |
+ log.L.WithError(err). |
|
| 181 |
+ WithField("vip.network", vip.NetworkID).
|
|
| 182 |
+ WithField("vip.addr", vip.Addr).Error("error deallocating vip")
|
|
| 183 |
+ } |
|
| 184 |
+ } |
|
| 185 |
+ |
|
| 186 |
+ s.Endpoint.VirtualIPs = nil |
|
| 176 | 187 |
} |
| 188 |
+ |
|
| 189 |
+ delete(na.services, s.ID) |
|
| 190 |
+ return |
|
| 177 | 191 |
} |
| 178 | 192 |
|
| 179 | 193 |
// First allocate VIPs for all the pre-populated endpoint attachments |
| ... | ... |
@@ -198,7 +212,6 @@ outer: |
| 198 | 198 |
|
| 199 | 199 |
s.Endpoint.VirtualIPs = append(s.Endpoint.VirtualIPs, vip) |
| 200 | 200 |
} |
| 201 |
- s.Endpoint.Spec = s.Spec.Endpoint.Copy() |
|
| 202 | 201 |
|
| 203 | 202 |
na.services[s.ID] = struct{}{}
|
| 204 | 203 |
return |
| ... | ... |
@@ -232,7 +245,7 @@ func (na *NetworkAllocator) IsAllocated(n *api.Network) bool {
|
| 232 | 232 |
return ok |
| 233 | 233 |
} |
| 234 | 234 |
|
| 235 |
-// IsTaskAllocated returns if the passed task has it's network resources allocated or not. |
|
| 235 |
+// IsTaskAllocated returns if the passed task has its network resources allocated or not. |
|
| 236 | 236 |
func (na *NetworkAllocator) IsTaskAllocated(t *api.Task) bool {
|
| 237 | 237 |
// If the task is not found in the allocated set, then it is |
| 238 | 238 |
// not allocated. |
| ... | ... |
@@ -245,7 +258,7 @@ func (na *NetworkAllocator) IsTaskAllocated(t *api.Task) bool {
|
| 245 | 245 |
return false |
| 246 | 246 |
} |
| 247 | 247 |
|
| 248 |
- // To determine whether the task has it's resources allocated, |
|
| 248 |
+ // To determine whether the task has its resources allocated, |
|
| 249 | 249 |
// we just need to look at one network(in case of |
| 250 | 250 |
// multi-network attachment). This is because we make sure we |
| 251 | 251 |
// allocate for every network or we allocate for none. |
| ... | ... |
@@ -269,13 +282,30 @@ func (na *NetworkAllocator) IsTaskAllocated(t *api.Task) bool {
|
| 269 | 269 |
return true |
| 270 | 270 |
} |
| 271 | 271 |
|
| 272 |
-// IsServiceAllocated returns if the passed service has it's network resources allocated or not. |
|
| 272 |
+// IsServiceAllocated returns if the passed service has its network resources allocated or not. |
|
| 273 | 273 |
func (na *NetworkAllocator) IsServiceAllocated(s *api.Service) bool {
|
| 274 |
- if _, ok := na.services[s.ID]; !ok {
|
|
| 275 |
- return false |
|
| 274 |
+ // If endpoint mode is VIP and allocator does not have the |
|
| 275 |
+ // service in VIP allocated set then it is not allocated. |
|
| 276 |
+ if len(s.Spec.Networks) != 0 && |
|
| 277 |
+ (s.Spec.Endpoint == nil || |
|
| 278 |
+ s.Spec.Endpoint.Mode == api.ResolutionModeVirtualIP) {
|
|
| 279 |
+ if _, ok := na.services[s.ID]; !ok {
|
|
| 280 |
+ return false |
|
| 281 |
+ } |
|
| 282 |
+ } |
|
| 283 |
+ |
|
| 284 |
+ // If the endpoint mode is DNSRR and allocator has the service |
|
| 285 |
+ // in VIP allocated set then we return not allocated to make |
|
| 286 |
+ // sure the allocator triggers networkallocator to free up the |
|
| 287 |
+ // resources if any. |
|
| 288 |
+ if s.Spec.Endpoint != nil && s.Spec.Endpoint.Mode == api.ResolutionModeDNSRoundRobin {
|
|
| 289 |
+ if _, ok := na.services[s.ID]; ok {
|
|
| 290 |
+ return false |
|
| 291 |
+ } |
|
| 276 | 292 |
} |
| 277 | 293 |
|
| 278 |
- if s.Spec.Endpoint != nil {
|
|
| 294 |
+ if (s.Spec.Endpoint != nil && len(s.Spec.Endpoint.Ports) != 0) || |
|
| 295 |
+ (s.Endpoint != nil && len(s.Endpoint.Ports) != 0) {
|
|
| 279 | 296 |
return na.portAllocator.isPortsAllocated(s) |
| 280 | 297 |
} |
| 281 | 298 |
|
| ... | ... |
@@ -186,7 +186,7 @@ func (s *Server) ListClusters(ctx context.Context, request *api.ListClustersRequ |
| 186 | 186 |
} |
| 187 | 187 |
|
| 188 | 188 |
// redactClusters is a method that enforces a whitelist of fields that are ok to be |
| 189 |
-// returned in the Cluster object. It should filter out all senstive information. |
|
| 189 |
+// returned in the Cluster object. It should filter out all sensitive information. |
|
| 190 | 190 |
func redactClusters(clusters []*api.Cluster) []*api.Cluster {
|
| 191 | 191 |
var redactedClusters []*api.Cluster |
| 192 | 192 |
// Only add public fields to the new clusters |
| 193 | 193 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,12 @@ |
| 0 |
+package hackpicker |
|
| 1 |
+ |
|
| 2 |
+// AddrSelector is interface which should track cluster for its leader address. |
|
| 3 |
+type AddrSelector interface {
|
|
| 4 |
+ LeaderAddr() (string, error) |
|
| 5 |
+} |
|
| 6 |
+ |
|
| 7 |
+// RaftCluster is interface which combines useful methods for clustering. |
|
| 8 |
+type RaftCluster interface {
|
|
| 9 |
+ AddrSelector |
|
| 10 |
+ IsLeader() bool |
|
| 11 |
+} |
| 0 | 12 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,141 @@ |
| 0 |
+// Package hackpicker is temporary solution to provide more seamless experience |
|
| 1 |
+// for controlapi. It has drawback of slow reaction to leader change, but it |
|
| 2 |
+// tracks leader automatically without erroring out to client. |
|
| 3 |
+package hackpicker |
|
| 4 |
+ |
|
| 5 |
+import ( |
|
| 6 |
+ "sync" |
|
| 7 |
+ |
|
| 8 |
+ "golang.org/x/net/context" |
|
| 9 |
+ "google.golang.org/grpc" |
|
| 10 |
+ "google.golang.org/grpc/transport" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+// picker always picks address of cluster leader. |
|
| 14 |
+type picker struct {
|
|
| 15 |
+ mu sync.Mutex |
|
| 16 |
+ addr string |
|
| 17 |
+ raft AddrSelector |
|
| 18 |
+ conn *grpc.Conn |
|
| 19 |
+ cc *grpc.ClientConn |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+// Init does initial processing for the Picker, e.g., initiate some connections. |
|
| 23 |
+func (p *picker) Init(cc *grpc.ClientConn) error {
|
|
| 24 |
+ p.cc = cc |
|
| 25 |
+ return nil |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+func (p *picker) initConn() error {
|
|
| 29 |
+ if p.conn == nil {
|
|
| 30 |
+ conn, err := grpc.NewConn(p.cc) |
|
| 31 |
+ if err != nil {
|
|
| 32 |
+ return err |
|
| 33 |
+ } |
|
| 34 |
+ p.conn = conn |
|
| 35 |
+ } |
|
| 36 |
+ return nil |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+// Pick blocks until either a transport.ClientTransport is ready for the upcoming RPC |
|
| 40 |
+// or some error happens. |
|
| 41 |
+func (p *picker) Pick(ctx context.Context) (transport.ClientTransport, error) {
|
|
| 42 |
+ p.mu.Lock() |
|
| 43 |
+ if err := p.initConn(); err != nil {
|
|
| 44 |
+ p.mu.Unlock() |
|
| 45 |
+ return nil, err |
|
| 46 |
+ } |
|
| 47 |
+ p.mu.Unlock() |
|
| 48 |
+ |
|
| 49 |
+ addr, err := p.raft.LeaderAddr() |
|
| 50 |
+ if err != nil {
|
|
| 51 |
+ return nil, err |
|
| 52 |
+ } |
|
| 53 |
+ p.mu.Lock() |
|
| 54 |
+ if p.addr != addr {
|
|
| 55 |
+ p.addr = addr |
|
| 56 |
+ p.conn.NotifyReset() |
|
| 57 |
+ } |
|
| 58 |
+ p.mu.Unlock() |
|
| 59 |
+ return p.conn.Wait(ctx) |
|
| 60 |
+} |
|
| 61 |
+ |
|
| 62 |
+// PickAddr picks a peer address for connecting. This will be called repeated for |
|
| 63 |
+// connecting/reconnecting. |
|
| 64 |
+func (p *picker) PickAddr() (string, error) {
|
|
| 65 |
+ addr, err := p.raft.LeaderAddr() |
|
| 66 |
+ if err != nil {
|
|
| 67 |
+ return "", err |
|
| 68 |
+ } |
|
| 69 |
+ p.mu.Lock() |
|
| 70 |
+ p.addr = addr |
|
| 71 |
+ p.mu.Unlock() |
|
| 72 |
+ return addr, nil |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+// State returns the connectivity state of the underlying connections. |
|
| 76 |
+func (p *picker) State() (grpc.ConnectivityState, error) {
|
|
| 77 |
+ return p.conn.State(), nil |
|
| 78 |
+} |
|
| 79 |
+ |
|
| 80 |
+// WaitForStateChange blocks until the state changes to something other than |
|
| 81 |
+// the sourceState. It returns the new state or error. |
|
| 82 |
+func (p *picker) WaitForStateChange(ctx context.Context, sourceState grpc.ConnectivityState) (grpc.ConnectivityState, error) {
|
|
| 83 |
+ return p.conn.WaitForStateChange(ctx, sourceState) |
|
| 84 |
+} |
|
| 85 |
+ |
|
| 86 |
+// Reset the current connection and force a reconnect to another address. |
|
| 87 |
+func (p *picker) Reset() error {
|
|
| 88 |
+ p.conn.NotifyReset() |
|
| 89 |
+ return nil |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+// Close closes all the Conn's owned by this Picker. |
|
| 93 |
+func (p *picker) Close() error {
|
|
| 94 |
+ return p.conn.Close() |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+// ConnSelector is struct for obtaining connection with raftpicker. |
|
| 98 |
+type ConnSelector struct {
|
|
| 99 |
+ mu sync.Mutex |
|
| 100 |
+ cc *grpc.ClientConn |
|
| 101 |
+ cluster RaftCluster |
|
| 102 |
+ opts []grpc.DialOption |
|
| 103 |
+} |
|
| 104 |
+ |
|
| 105 |
+// NewConnSelector returns new ConnSelector with cluster and grpc.DialOpts which |
|
| 106 |
+// will be used for Dial on first call of Conn. |
|
| 107 |
+func NewConnSelector(cluster RaftCluster, opts ...grpc.DialOption) *ConnSelector {
|
|
| 108 |
+ return &ConnSelector{
|
|
| 109 |
+ cluster: cluster, |
|
| 110 |
+ opts: opts, |
|
| 111 |
+ } |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+// Conn returns *grpc.ClientConn with picker which picks raft cluster leader. |
|
| 115 |
+// Internal connection estabilished lazily on this call. |
|
| 116 |
+// It can return error if cluster wasn't ready at the moment of initial call. |
|
| 117 |
+func (c *ConnSelector) Conn() (*grpc.ClientConn, error) {
|
|
| 118 |
+ c.mu.Lock() |
|
| 119 |
+ defer c.mu.Unlock() |
|
| 120 |
+ if c.cc != nil {
|
|
| 121 |
+ return c.cc, nil |
|
| 122 |
+ } |
|
| 123 |
+ addr, err := c.cluster.LeaderAddr() |
|
| 124 |
+ if err != nil {
|
|
| 125 |
+ return nil, err |
|
| 126 |
+ } |
|
| 127 |
+ picker := &picker{raft: c.cluster, addr: addr}
|
|
| 128 |
+ opts := append(c.opts, grpc.WithPicker(picker)) |
|
| 129 |
+ cc, err := grpc.Dial(addr, opts...) |
|
| 130 |
+ if err != nil {
|
|
| 131 |
+ return nil, err |
|
| 132 |
+ } |
|
| 133 |
+ c.cc = cc |
|
| 134 |
+ return c.cc, nil |
|
| 135 |
+} |
|
| 136 |
+ |
|
| 137 |
+// Reset does nothing for hackpicker. |
|
| 138 |
+func (c *ConnSelector) Reset() error {
|
|
| 139 |
+ return nil |
|
| 140 |
+} |
| ... | ... |
@@ -182,7 +182,7 @@ func validateServiceSpec(spec *api.ServiceSpec) error {
|
| 182 | 182 |
|
| 183 | 183 |
// checkPortConflicts does a best effort to find if the passed in spec has port |
| 184 | 184 |
// conflicts with existing services. |
| 185 |
-func (s *Server) checkPortConflicts(spec *api.ServiceSpec) error {
|
|
| 185 |
+func (s *Server) checkPortConflicts(spec *api.ServiceSpec, serviceID string) error {
|
|
| 186 | 186 |
if spec.Endpoint == nil {
|
| 187 | 187 |
return nil |
| 188 | 188 |
} |
| ... | ... |
@@ -215,17 +215,21 @@ func (s *Server) checkPortConflicts(spec *api.ServiceSpec) error {
|
| 215 | 215 |
} |
| 216 | 216 |
|
| 217 | 217 |
for _, service := range services {
|
| 218 |
+ // If service ID is the same (and not "") then this is an update |
|
| 219 |
+ if serviceID != "" && serviceID == service.ID {
|
|
| 220 |
+ continue |
|
| 221 |
+ } |
|
| 218 | 222 |
if service.Spec.Endpoint != nil {
|
| 219 | 223 |
for _, pc := range service.Spec.Endpoint.Ports {
|
| 220 | 224 |
if reqPorts[pcToString(pc)] {
|
| 221 |
- return grpc.Errorf(codes.InvalidArgument, "port '%d' is already in use by service %s", pc.PublishedPort, service.ID) |
|
| 225 |
+ return grpc.Errorf(codes.InvalidArgument, "port '%d' is already in use by service '%s' (%s)", pc.PublishedPort, service.Spec.Annotations.Name, service.ID) |
|
| 222 | 226 |
} |
| 223 | 227 |
} |
| 224 | 228 |
} |
| 225 | 229 |
if service.Endpoint != nil {
|
| 226 | 230 |
for _, pc := range service.Endpoint.Ports {
|
| 227 | 231 |
if reqPorts[pcToString(pc)] {
|
| 228 |
- return grpc.Errorf(codes.InvalidArgument, "port '%d' is already in use by service %s", pc.PublishedPort, service.ID) |
|
| 232 |
+ return grpc.Errorf(codes.InvalidArgument, "port '%d' is already in use by service '%s' (%s)", pc.PublishedPort, service.Spec.Annotations.Name, service.ID) |
|
| 229 | 233 |
} |
| 230 | 234 |
} |
| 231 | 235 |
} |
| ... | ... |
@@ -243,7 +247,7 @@ func (s *Server) CreateService(ctx context.Context, request *api.CreateServiceRe |
| 243 | 243 |
return nil, err |
| 244 | 244 |
} |
| 245 | 245 |
|
| 246 |
- if err := s.checkPortConflicts(request.Spec); err != nil {
|
|
| 246 |
+ if err := s.checkPortConflicts(request.Spec, ""); err != nil {
|
|
| 247 | 247 |
return nil, err |
| 248 | 248 |
} |
| 249 | 249 |
|
| ... | ... |
@@ -309,7 +313,7 @@ func (s *Server) UpdateService(ctx context.Context, request *api.UpdateServiceRe |
| 309 | 309 |
} |
| 310 | 310 |
|
| 311 | 311 |
if request.Spec.Endpoint != nil && !reflect.DeepEqual(request.Spec.Endpoint, service.Spec.Endpoint) {
|
| 312 |
- if err := s.checkPortConflicts(request.Spec); err != nil {
|
|
| 312 |
+ if err := s.checkPortConflicts(request.Spec, request.ServiceID); err != nil {
|
|
| 313 | 313 |
return nil, err |
| 314 | 314 |
} |
| 315 | 315 |
} |
| ... | ... |
@@ -59,7 +59,7 @@ var ( |
| 59 | 59 |
) |
| 60 | 60 |
|
| 61 | 61 |
// Config is configuration for Dispatcher. For default you should use |
| 62 |
-// DefautConfig. |
|
| 62 |
+// DefaultConfig. |
|
| 63 | 63 |
type Config struct {
|
| 64 | 64 |
HeartbeatPeriod time.Duration |
| 65 | 65 |
HeartbeatEpsilon time.Duration |
| ... | ... |
@@ -79,13 +79,20 @@ func DefaultConfig() *Config {
|
| 79 | 79 |
} |
| 80 | 80 |
} |
| 81 | 81 |
|
| 82 |
-// Cluster is interface which represent raft cluster. mananger/state/raft.Node |
|
| 83 |
-// is implenents it. This interface needed only for easier unit-testing. |
|
| 82 |
+// Cluster is interface which represent raft cluster. manager/state/raft.Node |
|
| 83 |
+// is implements it. This interface needed only for easier unit-testing. |
|
| 84 | 84 |
type Cluster interface {
|
| 85 | 85 |
GetMemberlist() map[uint64]*api.RaftMember |
| 86 | 86 |
MemoryStore() *store.MemoryStore |
| 87 | 87 |
} |
| 88 | 88 |
|
| 89 |
+// nodeUpdate provides a new status and/or description to apply to a node |
|
| 90 |
+// object. |
|
| 91 |
+type nodeUpdate struct {
|
|
| 92 |
+ status *api.NodeStatus |
|
| 93 |
+ description *api.NodeDescription |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 89 | 96 |
// Dispatcher is responsible for dispatching tasks and tracking agent health. |
| 90 | 97 |
type Dispatcher struct {
|
| 91 | 98 |
mu sync.Mutex |
| ... | ... |
@@ -103,7 +110,14 @@ type Dispatcher struct {
|
| 103 | 103 |
taskUpdates map[string]*api.TaskStatus // indexed by task ID |
| 104 | 104 |
taskUpdatesLock sync.Mutex |
| 105 | 105 |
|
| 106 |
- processTaskUpdatesTrigger chan struct{}
|
|
| 106 |
+ nodeUpdates map[string]nodeUpdate // indexed by node ID |
|
| 107 |
+ nodeUpdatesLock sync.Mutex |
|
| 108 |
+ |
|
| 109 |
+ processUpdatesTrigger chan struct{}
|
|
| 110 |
+ |
|
| 111 |
+ // for waiting for the next task/node batch update |
|
| 112 |
+ processUpdatesLock sync.Mutex |
|
| 113 |
+ processUpdatesCond *sync.Cond |
|
| 107 | 114 |
} |
| 108 | 115 |
|
| 109 | 116 |
// weightedPeerByNodeID is a sort wrapper for []*api.WeightedPeer |
| ... | ... |
@@ -118,16 +132,21 @@ func (b weightedPeerByNodeID) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
| 118 | 118 |
// New returns Dispatcher with cluster interface(usually raft.Node). |
| 119 | 119 |
// NOTE: each handler which does something with raft must add to Dispatcher.wg |
| 120 | 120 |
func New(cluster Cluster, c *Config) *Dispatcher {
|
| 121 |
- return &Dispatcher{
|
|
| 122 |
- nodes: newNodeStore(c.HeartbeatPeriod, c.HeartbeatEpsilon, c.GracePeriodMultiplier, c.RateLimitPeriod), |
|
| 123 |
- store: cluster.MemoryStore(), |
|
| 124 |
- cluster: cluster, |
|
| 125 |
- mgrQueue: watch.NewQueue(16), |
|
| 126 |
- keyMgrQueue: watch.NewQueue(16), |
|
| 127 |
- taskUpdates: make(map[string]*api.TaskStatus), |
|
| 128 |
- processTaskUpdatesTrigger: make(chan struct{}, 1),
|
|
| 129 |
- config: c, |
|
| 121 |
+ d := &Dispatcher{
|
|
| 122 |
+ nodes: newNodeStore(c.HeartbeatPeriod, c.HeartbeatEpsilon, c.GracePeriodMultiplier, c.RateLimitPeriod), |
|
| 123 |
+ store: cluster.MemoryStore(), |
|
| 124 |
+ cluster: cluster, |
|
| 125 |
+ mgrQueue: watch.NewQueue(16), |
|
| 126 |
+ keyMgrQueue: watch.NewQueue(16), |
|
| 127 |
+ taskUpdates: make(map[string]*api.TaskStatus), |
|
| 128 |
+ nodeUpdates: make(map[string]nodeUpdate), |
|
| 129 |
+ processUpdatesTrigger: make(chan struct{}, 1),
|
|
| 130 |
+ config: c, |
|
| 130 | 131 |
} |
| 132 |
+ |
|
| 133 |
+ d.processUpdatesCond = sync.NewCond(&d.processUpdatesLock) |
|
| 134 |
+ |
|
| 135 |
+ return d |
|
| 131 | 136 |
} |
| 132 | 137 |
|
| 133 | 138 |
func getWeightedPeers(cluster Cluster) []*api.WeightedPeer {
|
| ... | ... |
@@ -157,10 +176,9 @@ func (d *Dispatcher) Run(ctx context.Context) error {
|
| 157 | 157 |
d.mu.Unlock() |
| 158 | 158 |
return fmt.Errorf("dispatcher is already running")
|
| 159 | 159 |
} |
| 160 |
- logger := log.G(ctx).WithField("module", "dispatcher")
|
|
| 161 |
- ctx = log.WithLogger(ctx, logger) |
|
| 160 |
+ ctx = log.WithModule(ctx, "dispatcher") |
|
| 162 | 161 |
if err := d.markNodesUnknown(ctx); err != nil {
|
| 163 |
- logger.Errorf(`failed to move all nodes to "unknown" state: %v`, err) |
|
| 162 |
+ log.G(ctx).Errorf(`failed to move all nodes to "unknown" state: %v`, err) |
|
| 164 | 163 |
} |
| 165 | 164 |
configWatcher, cancel, err := store.ViewAndWatch( |
| 166 | 165 |
d.store, |
| ... | ... |
@@ -214,11 +232,11 @@ func (d *Dispatcher) Run(ctx context.Context) error {
|
| 214 | 214 |
select {
|
| 215 | 215 |
case <-publishTicker.C: |
| 216 | 216 |
publishManagers() |
| 217 |
- case <-d.processTaskUpdatesTrigger: |
|
| 218 |
- d.processTaskUpdates() |
|
| 217 |
+ case <-d.processUpdatesTrigger: |
|
| 218 |
+ d.processUpdates() |
|
| 219 | 219 |
batchTimer.Reset(maxBatchInterval) |
| 220 | 220 |
case <-batchTimer.C: |
| 221 |
- d.processTaskUpdates() |
|
| 221 |
+ d.processUpdates() |
|
| 222 | 222 |
batchTimer.Reset(maxBatchInterval) |
| 223 | 223 |
case v := <-configWatcher: |
| 224 | 224 |
cluster := v.(state.EventUpdateCluster) |
| ... | ... |
@@ -251,6 +269,14 @@ func (d *Dispatcher) Stop() error {
|
| 251 | 251 |
d.cancel() |
| 252 | 252 |
d.mu.Unlock() |
| 253 | 253 |
d.nodes.Clean() |
| 254 |
+ |
|
| 255 |
+ d.processUpdatesLock.Lock() |
|
| 256 |
+ // In case there are any waiters. There is no chance of any starting |
|
| 257 |
+ // after this point, because they check if the context is canceled |
|
| 258 |
+ // before waiting. |
|
| 259 |
+ d.processUpdatesCond.Broadcast() |
|
| 260 |
+ d.processUpdatesLock.Unlock() |
|
| 261 |
+ |
|
| 254 | 262 |
return nil |
| 255 | 263 |
} |
| 256 | 264 |
|
| ... | ... |
@@ -340,26 +366,39 @@ func (d *Dispatcher) register(ctx context.Context, nodeID string, description *a |
| 340 | 340 |
return "", err |
| 341 | 341 |
} |
| 342 | 342 |
|
| 343 |
- // create or update node in store |
|
| 344 | 343 |
// TODO(stevvooe): Validate node specification. |
| 345 | 344 |
var node *api.Node |
| 346 |
- err := d.store.Update(func(tx store.Tx) error {
|
|
| 345 |
+ d.store.View(func(tx store.ReadTx) {
|
|
| 347 | 346 |
node = store.GetNode(tx, nodeID) |
| 348 |
- if node == nil {
|
|
| 349 |
- return ErrNodeNotFound |
|
| 350 |
- } |
|
| 347 |
+ }) |
|
| 348 |
+ if node == nil {
|
|
| 349 |
+ return "", ErrNodeNotFound |
|
| 350 |
+ } |
|
| 351 | 351 |
|
| 352 |
- node.Description = description |
|
| 353 |
- node.Status = api.NodeStatus{
|
|
| 354 |
- State: api.NodeStatus_READY, |
|
| 352 |
+ d.nodeUpdatesLock.Lock() |
|
| 353 |
+ d.nodeUpdates[nodeID] = nodeUpdate{status: &api.NodeStatus{State: api.NodeStatus_READY}, description: description}
|
|
| 354 |
+ numUpdates := len(d.nodeUpdates) |
|
| 355 |
+ d.nodeUpdatesLock.Unlock() |
|
| 356 |
+ |
|
| 357 |
+ if numUpdates >= maxBatchItems {
|
|
| 358 |
+ select {
|
|
| 359 |
+ case d.processUpdatesTrigger <- struct{}{}:
|
|
| 360 |
+ case <-d.ctx.Done(): |
|
| 361 |
+ return "", d.ctx.Err() |
|
| 355 | 362 |
} |
| 356 |
- return store.UpdateNode(tx, node) |
|
| 357 | 363 |
|
| 358 |
- }) |
|
| 359 |
- if err != nil {
|
|
| 360 |
- return "", err |
|
| 361 | 364 |
} |
| 362 | 365 |
|
| 366 |
+ // Wait until the node update batch happens before unblocking register. |
|
| 367 |
+ d.processUpdatesLock.Lock() |
|
| 368 |
+ select {
|
|
| 369 |
+ case <-d.ctx.Done(): |
|
| 370 |
+ return "", d.ctx.Err() |
|
| 371 |
+ default: |
|
| 372 |
+ } |
|
| 373 |
+ d.processUpdatesCond.Wait() |
|
| 374 |
+ d.processUpdatesLock.Unlock() |
|
| 375 |
+ |
|
| 363 | 376 |
expireFunc := func() {
|
| 364 | 377 |
nodeStatus := api.NodeStatus{State: api.NodeStatus_DOWN, Message: "heartbeat failure"}
|
| 365 | 378 |
log.G(ctx).Debugf("heartbeat expiration")
|
| ... | ... |
@@ -444,23 +483,39 @@ func (d *Dispatcher) UpdateTaskStatus(ctx context.Context, r *api.UpdateTaskStat |
| 444 | 444 |
d.taskUpdatesLock.Unlock() |
| 445 | 445 |
|
| 446 | 446 |
if numUpdates >= maxBatchItems {
|
| 447 |
- d.processTaskUpdatesTrigger <- struct{}{}
|
|
| 447 |
+ select {
|
|
| 448 |
+ case d.processUpdatesTrigger <- struct{}{}:
|
|
| 449 |
+ case <-d.ctx.Done(): |
|
| 450 |
+ } |
|
| 448 | 451 |
} |
| 449 | 452 |
return nil, nil |
| 450 | 453 |
} |
| 451 | 454 |
|
| 452 |
-func (d *Dispatcher) processTaskUpdates() {
|
|
| 455 |
+func (d *Dispatcher) processUpdates() {
|
|
| 456 |
+ var ( |
|
| 457 |
+ taskUpdates map[string]*api.TaskStatus |
|
| 458 |
+ nodeUpdates map[string]nodeUpdate |
|
| 459 |
+ ) |
|
| 453 | 460 |
d.taskUpdatesLock.Lock() |
| 454 |
- if len(d.taskUpdates) == 0 {
|
|
| 455 |
- d.taskUpdatesLock.Unlock() |
|
| 456 |
- return |
|
| 461 |
+ if len(d.taskUpdates) != 0 {
|
|
| 462 |
+ taskUpdates = d.taskUpdates |
|
| 463 |
+ d.taskUpdates = make(map[string]*api.TaskStatus) |
|
| 457 | 464 |
} |
| 458 |
- taskUpdates := d.taskUpdates |
|
| 459 |
- d.taskUpdates = make(map[string]*api.TaskStatus) |
|
| 460 | 465 |
d.taskUpdatesLock.Unlock() |
| 461 | 466 |
|
| 467 |
+ d.nodeUpdatesLock.Lock() |
|
| 468 |
+ if len(d.nodeUpdates) != 0 {
|
|
| 469 |
+ nodeUpdates = d.nodeUpdates |
|
| 470 |
+ d.nodeUpdates = make(map[string]nodeUpdate) |
|
| 471 |
+ } |
|
| 472 |
+ d.nodeUpdatesLock.Unlock() |
|
| 473 |
+ |
|
| 474 |
+ if len(taskUpdates) == 0 && len(nodeUpdates) == 0 {
|
|
| 475 |
+ return |
|
| 476 |
+ } |
|
| 477 |
+ |
|
| 462 | 478 |
log := log.G(d.ctx).WithFields(logrus.Fields{
|
| 463 |
- "method": "(*Dispatcher).processTaskUpdates", |
|
| 479 |
+ "method": "(*Dispatcher).processUpdates", |
|
| 464 | 480 |
}) |
| 465 | 481 |
|
| 466 | 482 |
_, err := d.store.Batch(func(batch *store.Batch) error {
|
| ... | ... |
@@ -494,14 +549,45 @@ func (d *Dispatcher) processTaskUpdates() {
|
| 494 | 494 |
return nil |
| 495 | 495 |
}) |
| 496 | 496 |
if err != nil {
|
| 497 |
- log.WithError(err).Error("dispatcher transaction failed")
|
|
| 497 |
+ log.WithError(err).Error("dispatcher task update transaction failed")
|
|
| 498 |
+ } |
|
| 499 |
+ } |
|
| 500 |
+ |
|
| 501 |
+ for nodeID, nodeUpdate := range nodeUpdates {
|
|
| 502 |
+ err := batch.Update(func(tx store.Tx) error {
|
|
| 503 |
+ logger := log.WithField("node.id", nodeID)
|
|
| 504 |
+ node := store.GetNode(tx, nodeID) |
|
| 505 |
+ if node == nil {
|
|
| 506 |
+ logger.Errorf("node unavailable")
|
|
| 507 |
+ return nil |
|
| 508 |
+ } |
|
| 509 |
+ |
|
| 510 |
+ if nodeUpdate.status != nil {
|
|
| 511 |
+ node.Status = *nodeUpdate.status |
|
| 512 |
+ } |
|
| 513 |
+ if nodeUpdate.description != nil {
|
|
| 514 |
+ node.Description = nodeUpdate.description |
|
| 515 |
+ } |
|
| 516 |
+ |
|
| 517 |
+ if err := store.UpdateNode(tx, node); err != nil {
|
|
| 518 |
+ logger.WithError(err).Error("failed to update node status")
|
|
| 519 |
+ return nil |
|
| 520 |
+ } |
|
| 521 |
+ logger.Debug("node status updated")
|
|
| 522 |
+ return nil |
|
| 523 |
+ }) |
|
| 524 |
+ if err != nil {
|
|
| 525 |
+ log.WithError(err).Error("dispatcher node update transaction failed")
|
|
| 498 | 526 |
} |
| 499 | 527 |
} |
| 528 |
+ |
|
| 500 | 529 |
return nil |
| 501 | 530 |
}) |
| 502 | 531 |
if err != nil {
|
| 503 | 532 |
log.WithError(err).Error("dispatcher batch failed")
|
| 504 | 533 |
} |
| 534 |
+ |
|
| 535 |
+ d.processUpdatesCond.Broadcast() |
|
| 505 | 536 |
} |
| 506 | 537 |
|
| 507 | 538 |
// Tasks is a stream of tasks state for node. Each message contains full list |
| ... | ... |
@@ -595,7 +681,10 @@ func (d *Dispatcher) Tasks(r *api.TasksRequest, stream api.Dispatcher_TasksServe |
| 595 | 595 |
modificationCnt++ |
| 596 | 596 |
case state.EventUpdateTask: |
| 597 | 597 |
if oldTask, exists := tasksMap[v.Task.ID]; exists {
|
| 598 |
- if equality.TasksEqualStable(oldTask, v.Task) {
|
|
| 598 |
+ // States ASSIGNED and below are set by the orchestrator/scheduler, |
|
| 599 |
+ // not the agent, so tasks in these states need to be sent to the |
|
| 600 |
+ // agent even if nothing else has changed. |
|
| 601 |
+ if equality.TasksEqualStable(oldTask, v.Task) && v.Task.Status.State > api.TaskStateAssigned {
|
|
| 599 | 602 |
// this update should not trigger action at agent |
| 600 | 603 |
tasksMap[v.Task.ID] = v.Task |
| 601 | 604 |
continue |
| ... | ... |
@@ -632,17 +721,17 @@ func (d *Dispatcher) nodeRemove(id string, status api.NodeStatus) error {
|
| 632 | 632 |
if err := d.isRunningLocked(); err != nil {
|
| 633 | 633 |
return err |
| 634 | 634 |
} |
| 635 |
- // TODO(aaronl): Is it worth batching node removals? |
|
| 636 |
- err := d.store.Update(func(tx store.Tx) error {
|
|
| 637 |
- node := store.GetNode(tx, id) |
|
| 638 |
- if node == nil {
|
|
| 639 |
- return errors.New("node not found")
|
|
| 635 |
+ |
|
| 636 |
+ d.nodeUpdatesLock.Lock() |
|
| 637 |
+ d.nodeUpdates[id] = nodeUpdate{status: status.Copy(), description: d.nodeUpdates[id].description}
|
|
| 638 |
+ numUpdates := len(d.nodeUpdates) |
|
| 639 |
+ d.nodeUpdatesLock.Unlock() |
|
| 640 |
+ |
|
| 641 |
+ if numUpdates >= maxBatchItems {
|
|
| 642 |
+ select {
|
|
| 643 |
+ case d.processUpdatesTrigger <- struct{}{}:
|
|
| 644 |
+ case <-d.ctx.Done(): |
|
| 640 | 645 |
} |
| 641 |
- node.Status = status |
|
| 642 |
- return store.UpdateNode(tx, node) |
|
| 643 |
- }) |
|
| 644 |
- if err != nil {
|
|
| 645 |
- return fmt.Errorf("failed to update node %s status to down: %v", id, err)
|
|
| 646 | 646 |
} |
| 647 | 647 |
|
| 648 | 648 |
if rn := d.nodes.Delete(id); rn == nil {
|
| ... | ... |
@@ -122,7 +122,6 @@ func (k *KeyManager) updateKey(cluster *api.Cluster) error {
|
| 122 | 122 |
} |
| 123 | 123 |
|
| 124 | 124 |
func (k *KeyManager) rotateKey(ctx context.Context) error {
|
| 125 |
- log := log.G(ctx).WithField("module", "keymanager")
|
|
| 126 | 125 |
var ( |
| 127 | 126 |
clusters []*api.Cluster |
| 128 | 127 |
err error |
| ... | ... |
@@ -132,7 +131,7 @@ func (k *KeyManager) rotateKey(ctx context.Context) error {
|
| 132 | 132 |
}) |
| 133 | 133 |
|
| 134 | 134 |
if err != nil {
|
| 135 |
- log.Errorf("reading cluster config failed, %v", err)
|
|
| 135 |
+ log.G(ctx).Errorf("reading cluster config failed, %v", err)
|
|
| 136 | 136 |
return err |
| 137 | 137 |
} |
| 138 | 138 |
|
| ... | ... |
@@ -173,7 +172,7 @@ func (k *KeyManager) rotateKey(ctx context.Context) error {
|
| 173 | 173 |
// Run starts the keymanager, it doesn't return |
| 174 | 174 |
func (k *KeyManager) Run(ctx context.Context) error {
|
| 175 | 175 |
k.mu.Lock() |
| 176 |
- log := log.G(ctx).WithField("module", "keymanager")
|
|
| 176 |
+ ctx = log.WithModule(ctx, "keymanager") |
|
| 177 | 177 |
var ( |
| 178 | 178 |
clusters []*api.Cluster |
| 179 | 179 |
err error |
| ... | ... |
@@ -183,7 +182,7 @@ func (k *KeyManager) Run(ctx context.Context) error {
|
| 183 | 183 |
}) |
| 184 | 184 |
|
| 185 | 185 |
if err != nil {
|
| 186 |
- log.Errorf("reading cluster config failed, %v", err)
|
|
| 186 |
+ log.G(ctx).Errorf("reading cluster config failed, %v", err)
|
|
| 187 | 187 |
k.mu.Unlock() |
| 188 | 188 |
return err |
| 189 | 189 |
} |
| ... | ... |
@@ -196,7 +195,7 @@ func (k *KeyManager) Run(ctx context.Context) error {
|
| 196 | 196 |
} |
| 197 | 197 |
} |
| 198 | 198 |
if err := k.updateKey(cluster); err != nil {
|
| 199 |
- log.Errorf("store update failed %v", err)
|
|
| 199 |
+ log.G(ctx).Errorf("store update failed %v", err)
|
|
| 200 | 200 |
} |
| 201 | 201 |
} else {
|
| 202 | 202 |
k.keyRing.lClock = cluster.EncryptionKeyLamportClock |
| ... | ... |
@@ -17,6 +17,7 @@ import ( |
| 17 | 17 |
"github.com/docker/swarmkit/log" |
| 18 | 18 |
"github.com/docker/swarmkit/manager/allocator" |
| 19 | 19 |
"github.com/docker/swarmkit/manager/controlapi" |
| 20 |
+ "github.com/docker/swarmkit/manager/controlapi/hackpicker" |
|
| 20 | 21 |
"github.com/docker/swarmkit/manager/dispatcher" |
| 21 | 22 |
"github.com/docker/swarmkit/manager/health" |
| 22 | 23 |
"github.com/docker/swarmkit/manager/keymanager" |
| ... | ... |
@@ -350,11 +351,13 @@ func (m *Manager) Run(parent context.Context) error {
|
| 350 | 350 |
// creating the allocator but then use it anyway. |
| 351 | 351 |
} |
| 352 | 352 |
|
| 353 |
- go func(keyManager *keymanager.KeyManager) {
|
|
| 354 |
- if err := keyManager.Run(ctx); err != nil {
|
|
| 355 |
- log.G(ctx).WithError(err).Error("keymanager failed with an error")
|
|
| 356 |
- } |
|
| 357 |
- }(m.keyManager) |
|
| 353 |
+ if m.keyManager != nil {
|
|
| 354 |
+ go func(keyManager *keymanager.KeyManager) {
|
|
| 355 |
+ if err := keyManager.Run(ctx); err != nil {
|
|
| 356 |
+ log.G(ctx).WithError(err).Error("keymanager failed with an error")
|
|
| 357 |
+ } |
|
| 358 |
+ }(m.keyManager) |
|
| 359 |
+ } |
|
| 358 | 360 |
|
| 359 | 361 |
go func(d *dispatcher.Dispatcher) {
|
| 360 | 362 |
if err := d.Run(ctx); err != nil {
|
| ... | ... |
@@ -385,14 +388,17 @@ func (m *Manager) Run(parent context.Context) error {
|
| 385 | 385 |
log.G(ctx).WithError(err).Error("scheduler exited with an error")
|
| 386 | 386 |
} |
| 387 | 387 |
}(m.scheduler) |
| 388 |
+ |
|
| 388 | 389 |
go func(taskReaper *orchestrator.TaskReaper) {
|
| 389 | 390 |
taskReaper.Run() |
| 390 | 391 |
}(m.taskReaper) |
| 392 |
+ |
|
| 391 | 393 |
go func(orchestrator *orchestrator.ReplicatedOrchestrator) {
|
| 392 | 394 |
if err := orchestrator.Run(ctx); err != nil {
|
| 393 | 395 |
log.G(ctx).WithError(err).Error("replicated orchestrator exited with an error")
|
| 394 | 396 |
} |
| 395 | 397 |
}(m.replicatedOrchestrator) |
| 398 |
+ |
|
| 396 | 399 |
go func(globalOrchestrator *orchestrator.GlobalOrchestrator) {
|
| 397 | 400 |
if err := globalOrchestrator.Run(ctx); err != nil {
|
| 398 | 401 |
log.G(ctx).WithError(err).Error("global orchestrator exited with an error")
|
| ... | ... |
@@ -420,21 +426,34 @@ func (m *Manager) Run(parent context.Context) error {
|
| 420 | 420 |
m.scheduler.Stop() |
| 421 | 421 |
m.scheduler = nil |
| 422 | 422 |
|
| 423 |
- m.keyManager.Stop() |
|
| 424 |
- m.keyManager = nil |
|
| 423 |
+ if m.keyManager != nil {
|
|
| 424 |
+ m.keyManager.Stop() |
|
| 425 |
+ m.keyManager = nil |
|
| 426 |
+ } |
|
| 425 | 427 |
} |
| 426 | 428 |
m.mu.Unlock() |
| 427 | 429 |
} |
| 428 | 430 |
}() |
| 429 | 431 |
|
| 430 | 432 |
proxyOpts := []grpc.DialOption{
|
| 431 |
- grpc.WithBackoffMaxDelay(time.Second), |
|
| 433 |
+ grpc.WithTimeout(5 * time.Second), |
|
| 432 | 434 |
grpc.WithTransportCredentials(m.config.SecurityConfig.ClientTLSCreds), |
| 433 | 435 |
} |
| 434 | 436 |
|
| 435 | 437 |
cs := raftpicker.NewConnSelector(m.RaftNode, proxyOpts...) |
| 436 | 438 |
m.connSelector = cs |
| 437 | 439 |
|
| 440 |
+ // We need special connSelector for controlapi because it provides automatic |
|
| 441 |
+ // leader tracking. |
|
| 442 |
+ // Other APIs are using connSelector which errors out on leader change, but |
|
| 443 |
+ // allows to react quickly to reelections. |
|
| 444 |
+ controlAPIProxyOpts := []grpc.DialOption{
|
|
| 445 |
+ grpc.WithBackoffMaxDelay(time.Second), |
|
| 446 |
+ grpc.WithTransportCredentials(m.config.SecurityConfig.ClientTLSCreds), |
|
| 447 |
+ } |
|
| 448 |
+ |
|
| 449 |
+ controlAPIConnSelector := hackpicker.NewConnSelector(m.RaftNode, controlAPIProxyOpts...) |
|
| 450 |
+ |
|
| 438 | 451 |
authorize := func(ctx context.Context, roles []string) error {
|
| 439 | 452 |
// Authorize the remote roles, ensure they can only be forwarded by managers |
| 440 | 453 |
_, err := ca.AuthorizeForwardedRoleAndOrg(ctx, roles, []string{ca.ManagerRole}, m.config.SecurityConfig.ClientTLSCreds.Organization())
|
| ... | ... |
@@ -464,7 +483,7 @@ func (m *Manager) Run(parent context.Context) error {
|
| 464 | 464 |
// this manager rather than forwarded requests (it has no TLS |
| 465 | 465 |
// information to put in the metadata map). |
| 466 | 466 |
forwardAsOwnRequest := func(ctx context.Context) (context.Context, error) { return ctx, nil }
|
| 467 |
- localProxyControlAPI := api.NewRaftProxyControlServer(baseControlAPI, cs, m.RaftNode, forwardAsOwnRequest) |
|
| 467 |
+ localProxyControlAPI := api.NewRaftProxyControlServer(baseControlAPI, controlAPIConnSelector, m.RaftNode, forwardAsOwnRequest) |
|
| 468 | 468 |
|
| 469 | 469 |
// Everything registered on m.server should be an authenticated |
| 470 | 470 |
// wrapper, or a proxy wrapping an authenticated wrapper! |
| ... | ... |
@@ -2,6 +2,7 @@ package orchestrator |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"container/list" |
| 5 |
+ "errors" |
|
| 5 | 6 |
"sync" |
| 6 | 7 |
"time" |
| 7 | 8 |
|
| ... | ... |
@@ -76,6 +77,9 @@ func (r *RestartSupervisor) waitRestart(ctx context.Context, oldDelay *delayedSt |
| 76 | 76 |
if t == nil {
|
| 77 | 77 |
return nil |
| 78 | 78 |
} |
| 79 |
+ if t.DesiredState > api.TaskStateRunning {
|
|
| 80 |
+ return nil |
|
| 81 |
+ } |
|
| 79 | 82 |
service := store.GetService(tx, t.ServiceID) |
| 80 | 83 |
if service == nil {
|
| 81 | 84 |
return nil |
| ... | ... |
@@ -108,6 +112,13 @@ func (r *RestartSupervisor) Restart(ctx context.Context, tx store.Tx, cluster *a |
| 108 | 108 |
} |
| 109 | 109 |
r.mu.Unlock() |
| 110 | 110 |
|
| 111 |
+ // Sanity check: was the task shut down already by a separate call to |
|
| 112 |
+ // Restart? If so, we must avoid restarting it, because this will create |
|
| 113 |
+ // an extra task. This should never happen unless there is a bug. |
|
| 114 |
+ if t.DesiredState > api.TaskStateRunning {
|
|
| 115 |
+ return errors.New("Restart called on task that was already shut down")
|
|
| 116 |
+ } |
|
| 117 |
+ |
|
| 111 | 118 |
t.DesiredState = api.TaskStateShutdown |
| 112 | 119 |
err := store.UpdateTask(tx, &t) |
| 113 | 120 |
if err != nil {
|
| ... | ... |
@@ -163,7 +163,7 @@ func (r *ReplicatedOrchestrator) tickTasks(ctx context.Context) {
|
| 163 | 163 |
}) |
| 164 | 164 |
|
| 165 | 165 |
if err != nil {
|
| 166 |
- log.G(ctx).WithError(err).Errorf("orchestator task removal batch failed")
|
|
| 166 |
+ log.G(ctx).WithError(err).Errorf("orchestrator task removal batch failed")
|
|
| 167 | 167 |
} |
| 168 | 168 |
|
| 169 | 169 |
r.restartTasks = make(map[string]struct{})
|
| ... | ... |
@@ -4,136 +4,43 @@ import ( |
| 4 | 4 |
"sync" |
| 5 | 5 |
"time" |
| 6 | 6 |
|
| 7 |
- "golang.org/x/net/context" |
|
| 7 |
+ "github.com/Sirupsen/logrus" |
|
| 8 |
+ |
|
| 8 | 9 |
"google.golang.org/grpc" |
| 9 |
- "google.golang.org/grpc/transport" |
|
| 10 | 10 |
) |
| 11 | 11 |
|
| 12 |
-// picker always picks address of cluster leader. |
|
| 13 |
-type picker struct {
|
|
| 14 |
- mu sync.Mutex |
|
| 15 |
- addr string |
|
| 16 |
- raft AddrSelector |
|
| 17 |
- conn *grpc.Conn |
|
| 18 |
- |
|
| 19 |
- stop chan struct{}
|
|
| 20 |
- done chan struct{}
|
|
| 21 |
-} |
|
| 22 |
- |
|
| 23 |
-func newPicker(raft AddrSelector, addr string) *picker {
|
|
| 24 |
- return &picker{
|
|
| 25 |
- raft: raft, |
|
| 26 |
- addr: addr, |
|
| 27 |
- |
|
| 28 |
- stop: make(chan struct{}),
|
|
| 29 |
- done: make(chan struct{}),
|
|
| 30 |
- } |
|
| 31 |
-} |
|
| 32 |
- |
|
| 33 |
-// Init does initial processing for the Picker, e.g., initiate some connections. |
|
| 34 |
-func (p *picker) Init(cc *grpc.ClientConn) error {
|
|
| 35 |
- conn, err := grpc.NewConn(cc) |
|
| 36 |
- if err != nil {
|
|
| 37 |
- return err |
|
| 38 |
- } |
|
| 39 |
- p.conn = conn |
|
| 40 |
- return nil |
|
| 41 |
-} |
|
| 42 |
- |
|
| 43 |
-// Pick blocks until either a transport.ClientTransport is ready for the upcoming RPC |
|
| 44 |
-// or some error happens. |
|
| 45 |
-func (p *picker) Pick(ctx context.Context) (transport.ClientTransport, error) {
|
|
| 46 |
- if err := p.updateConn(); err != nil {
|
|
| 47 |
- return nil, err |
|
| 48 |
- } |
|
| 49 |
- return p.conn.Wait(ctx) |
|
| 50 |
-} |
|
| 51 |
- |
|
| 52 |
-// PickAddr picks a peer address for connecting. This will be called repeated for |
|
| 53 |
-// connecting/reconnecting. |
|
| 54 |
-func (p *picker) PickAddr() (string, error) {
|
|
| 55 |
- addr, err := p.raft.LeaderAddr() |
|
| 56 |
- if err != nil {
|
|
| 57 |
- return "", err |
|
| 58 |
- } |
|
| 59 |
- p.mu.Lock() |
|
| 60 |
- p.addr = addr |
|
| 61 |
- p.mu.Unlock() |
|
| 62 |
- return addr, nil |
|
| 63 |
-} |
|
| 64 |
- |
|
| 65 |
-// State returns the connectivity state of the underlying connections. |
|
| 66 |
-func (p *picker) State() (grpc.ConnectivityState, error) {
|
|
| 67 |
- return p.conn.State(), nil |
|
| 68 |
-} |
|
| 69 |
- |
|
| 70 |
-// WaitForStateChange blocks until the state changes to something other than |
|
| 71 |
-// the sourceState. It returns the new state or error. |
|
| 72 |
-func (p *picker) WaitForStateChange(ctx context.Context, sourceState grpc.ConnectivityState) (grpc.ConnectivityState, error) {
|
|
| 73 |
- return p.conn.WaitForStateChange(ctx, sourceState) |
|
| 74 |
-} |
|
| 75 |
- |
|
| 76 |
-// Reset the current connection and force a reconnect to another address. |
|
| 77 |
-func (p *picker) Reset() error {
|
|
| 78 |
- p.conn.NotifyReset() |
|
| 79 |
- return nil |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-// Close closes all the Conn's owned by this Picker. |
|
| 83 |
-func (p *picker) Close() error {
|
|
| 84 |
- close(p.stop) |
|
| 85 |
- <-p.done |
|
| 86 |
- return p.conn.Close() |
|
| 87 |
-} |
|
| 88 |
- |
|
| 89 |
-func (p *picker) updateConn() error {
|
|
| 90 |
- addr, err := p.raft.LeaderAddr() |
|
| 91 |
- if err != nil {
|
|
| 92 |
- return err |
|
| 93 |
- } |
|
| 94 |
- p.mu.Lock() |
|
| 95 |
- if p.addr != addr {
|
|
| 96 |
- p.addr = addr |
|
| 97 |
- p.Reset() |
|
| 98 |
- } |
|
| 99 |
- p.mu.Unlock() |
|
| 100 |
- return nil |
|
| 101 |
-} |
|
| 102 |
- |
|
| 103 |
-func (p *picker) updateLoop() {
|
|
| 104 |
- defer close(p.done) |
|
| 105 |
- ticker := time.NewTicker(1 * time.Second) |
|
| 106 |
- defer ticker.Stop() |
|
| 107 |
- for {
|
|
| 108 |
- select {
|
|
| 109 |
- case <-ticker.C: |
|
| 110 |
- p.updateConn() |
|
| 111 |
- case <-p.stop: |
|
| 112 |
- return |
|
| 113 |
- } |
|
| 114 |
- } |
|
| 12 |
+// Interface is interface to replace implementation with controlapi/hackpicker. |
|
| 13 |
+// TODO: it should be done cooler. |
|
| 14 |
+type Interface interface {
|
|
| 15 |
+ Conn() (*grpc.ClientConn, error) |
|
| 16 |
+ Reset() error |
|
| 115 | 17 |
} |
| 116 | 18 |
|
| 117 |
-// ConnSelector is struct for obtaining connection with raftpicker. |
|
| 19 |
+// ConnSelector is struct for obtaining connection connected to cluster leader. |
|
| 118 | 20 |
type ConnSelector struct {
|
| 119 | 21 |
mu sync.Mutex |
| 120 |
- cc *grpc.ClientConn |
|
| 121 | 22 |
cluster RaftCluster |
| 122 | 23 |
opts []grpc.DialOption |
| 123 |
- picker *picker |
|
| 24 |
+ |
|
| 25 |
+ cc *grpc.ClientConn |
|
| 26 |
+ addr string |
|
| 27 |
+ |
|
| 28 |
+ stop chan struct{}
|
|
| 124 | 29 |
} |
| 125 | 30 |
|
| 126 | 31 |
// NewConnSelector returns new ConnSelector with cluster and grpc.DialOpts which |
| 127 |
-// will be used for Dial on first call of Conn. |
|
| 32 |
+// will be used for connection create. |
|
| 128 | 33 |
func NewConnSelector(cluster RaftCluster, opts ...grpc.DialOption) *ConnSelector {
|
| 129 |
- return &ConnSelector{
|
|
| 34 |
+ cs := &ConnSelector{
|
|
| 130 | 35 |
cluster: cluster, |
| 131 | 36 |
opts: opts, |
| 37 |
+ stop: make(chan struct{}),
|
|
| 132 | 38 |
} |
| 39 |
+ go cs.updateLoop() |
|
| 40 |
+ return cs |
|
| 133 | 41 |
} |
| 134 | 42 |
|
| 135 |
-// Conn returns *grpc.ClientConn with picker which picks raft cluster leader. |
|
| 136 |
-// Internal connection estabilished lazily on this call. |
|
| 43 |
+// Conn returns *grpc.ClientConn which connected to cluster leader. |
|
| 137 | 44 |
// It can return error if cluster wasn't ready at the moment of initial call. |
| 138 | 45 |
func (c *ConnSelector) Conn() (*grpc.ClientConn, error) {
|
| 139 | 46 |
c.mu.Lock() |
| ... | ... |
@@ -145,23 +52,76 @@ func (c *ConnSelector) Conn() (*grpc.ClientConn, error) {
|
| 145 | 145 |
if err != nil {
|
| 146 | 146 |
return nil, err |
| 147 | 147 |
} |
| 148 |
- c.picker = newPicker(c.cluster, addr) |
|
| 149 |
- go c.picker.updateLoop() |
|
| 150 |
- opts := append(c.opts, grpc.WithPicker(c.picker)) |
|
| 151 |
- cc, err := grpc.Dial(addr, opts...) |
|
| 148 |
+ cc, err := grpc.Dial(addr, c.opts...) |
|
| 152 | 149 |
if err != nil {
|
| 153 | 150 |
return nil, err |
| 154 | 151 |
} |
| 155 | 152 |
c.cc = cc |
| 156 |
- return c.cc, nil |
|
| 153 |
+ c.addr = addr |
|
| 154 |
+ return cc, nil |
|
| 157 | 155 |
} |
| 158 | 156 |
|
| 159 |
-// Stop cancels tracking loop for raftpicker and closes it. |
|
| 157 |
+// Reset recreates underlying connection. |
|
| 158 |
+func (c *ConnSelector) Reset() error {
|
|
| 159 |
+ c.mu.Lock() |
|
| 160 |
+ defer c.mu.Unlock() |
|
| 161 |
+ if c.cc != nil {
|
|
| 162 |
+ c.cc.Close() |
|
| 163 |
+ c.cc = nil |
|
| 164 |
+ } |
|
| 165 |
+ addr, err := c.cluster.LeaderAddr() |
|
| 166 |
+ if err != nil {
|
|
| 167 |
+ logrus.WithError(err).Errorf("error obtaining leader address")
|
|
| 168 |
+ return err |
|
| 169 |
+ } |
|
| 170 |
+ cc, err := grpc.Dial(addr, c.opts...) |
|
| 171 |
+ if err != nil {
|
|
| 172 |
+ logrus.WithError(err).Errorf("error reestabilishing connection to leader")
|
|
| 173 |
+ return err |
|
| 174 |
+ } |
|
| 175 |
+ c.cc = cc |
|
| 176 |
+ c.addr = addr |
|
| 177 |
+ return nil |
|
| 178 |
+} |
|
| 179 |
+ |
|
| 180 |
+// Stop cancels updating connection loop. |
|
| 160 | 181 |
func (c *ConnSelector) Stop() {
|
| 182 |
+ close(c.stop) |
|
| 183 |
+} |
|
| 184 |
+ |
|
| 185 |
+func (c *ConnSelector) updateConn() error {
|
|
| 186 |
+ addr, err := c.cluster.LeaderAddr() |
|
| 187 |
+ if err != nil {
|
|
| 188 |
+ return err |
|
| 189 |
+ } |
|
| 161 | 190 |
c.mu.Lock() |
| 162 | 191 |
defer c.mu.Unlock() |
| 163 |
- if c.picker == nil {
|
|
| 164 |
- return |
|
| 192 |
+ if c.addr != addr {
|
|
| 193 |
+ if c.cc != nil {
|
|
| 194 |
+ c.cc.Close() |
|
| 195 |
+ c.cc = nil |
|
| 196 |
+ } |
|
| 197 |
+ conn, err := grpc.Dial(addr, c.opts...) |
|
| 198 |
+ if err != nil {
|
|
| 199 |
+ return err |
|
| 200 |
+ } |
|
| 201 |
+ c.cc = conn |
|
| 202 |
+ c.addr = addr |
|
| 203 |
+ } |
|
| 204 |
+ return nil |
|
| 205 |
+} |
|
| 206 |
+ |
|
| 207 |
+func (c *ConnSelector) updateLoop() {
|
|
| 208 |
+ ticker := time.NewTicker(1 * time.Second) |
|
| 209 |
+ defer ticker.Stop() |
|
| 210 |
+ for {
|
|
| 211 |
+ select {
|
|
| 212 |
+ case <-ticker.C: |
|
| 213 |
+ if err := c.updateConn(); err != nil {
|
|
| 214 |
+ logrus.WithError(err).Errorf("error reestabilishing connection to leader")
|
|
| 215 |
+ } |
|
| 216 |
+ case <-c.stop: |
|
| 217 |
+ return |
|
| 218 |
+ } |
|
| 165 | 219 |
} |
| 166 |
- c.picker.Close() |
|
| 167 | 220 |
} |
| ... | ... |
@@ -61,8 +61,8 @@ func (s *Scheduler) setupTasksList(tx store.ReadTx) error {
|
| 61 | 61 |
tasksByNode := make(map[string]map[string]*api.Task) |
| 62 | 62 |
for _, t := range tasks {
|
| 63 | 63 |
// Ignore all tasks that have not reached ALLOCATED |
| 64 |
- // state. |
|
| 65 |
- if t.Status.State < api.TaskStateAllocated {
|
|
| 64 |
+ // state and tasks that no longer consume resources. |
|
| 65 |
+ if t.Status.State < api.TaskStateAllocated || t.Status.State > api.TaskStateRunning {
|
|
| 66 | 66 |
continue |
| 67 | 67 |
} |
| 68 | 68 |
|
| ... | ... |
@@ -109,8 +109,31 @@ func (s *Scheduler) Run(ctx context.Context) error {
|
| 109 | 109 |
// Queue all unassigned tasks before processing changes. |
| 110 | 110 |
s.tick(ctx) |
| 111 | 111 |
|
| 112 |
+ const ( |
|
| 113 |
+ // commitDebounceGap is the amount of time to wait between |
|
| 114 |
+ // commit events to debounce them. |
|
| 115 |
+ commitDebounceGap = 50 * time.Millisecond |
|
| 116 |
+ // maxLatency is a time limit on the debouncing. |
|
| 117 |
+ maxLatency = time.Second |
|
| 118 |
+ ) |
|
| 119 |
+ var ( |
|
| 120 |
+ debouncingStarted time.Time |
|
| 121 |
+ commitDebounceTimer *time.Timer |
|
| 122 |
+ commitDebounceTimeout <-chan time.Time |
|
| 123 |
+ ) |
|
| 124 |
+ |
|
| 112 | 125 |
pendingChanges := 0 |
| 113 | 126 |
|
| 127 |
+ schedule := func() {
|
|
| 128 |
+ if len(s.preassignedTasks) > 0 {
|
|
| 129 |
+ s.processPreassignedTasks(ctx) |
|
| 130 |
+ } |
|
| 131 |
+ if pendingChanges > 0 {
|
|
| 132 |
+ s.tick(ctx) |
|
| 133 |
+ pendingChanges = 0 |
|
| 134 |
+ } |
|
| 135 |
+ } |
|
| 136 |
+ |
|
| 114 | 137 |
// Watch for changes. |
| 115 | 138 |
for {
|
| 116 | 139 |
select {
|
| ... | ... |
@@ -131,15 +154,25 @@ func (s *Scheduler) Run(ctx context.Context) error {
|
| 131 | 131 |
case state.EventDeleteNode: |
| 132 | 132 |
s.nodeHeap.remove(v.Node.ID) |
| 133 | 133 |
case state.EventCommit: |
| 134 |
- if len(s.preassignedTasks) > 0 {
|
|
| 135 |
- s.processPreassignedTasks(ctx) |
|
| 136 |
- } |
|
| 137 |
- if pendingChanges > 0 {
|
|
| 138 |
- s.tick(ctx) |
|
| 139 |
- pendingChanges = 0 |
|
| 134 |
+ if commitDebounceTimer != nil {
|
|
| 135 |
+ if time.Since(debouncingStarted) > maxLatency {
|
|
| 136 |
+ commitDebounceTimer.Stop() |
|
| 137 |
+ commitDebounceTimer = nil |
|
| 138 |
+ commitDebounceTimeout = nil |
|
| 139 |
+ schedule() |
|
| 140 |
+ } else {
|
|
| 141 |
+ commitDebounceTimer.Reset(commitDebounceGap) |
|
| 142 |
+ } |
|
| 143 |
+ } else {
|
|
| 144 |
+ commitDebounceTimer = time.NewTimer(commitDebounceGap) |
|
| 145 |
+ commitDebounceTimeout = commitDebounceTimer.C |
|
| 146 |
+ debouncingStarted = time.Now() |
|
| 140 | 147 |
} |
| 141 | 148 |
} |
| 142 |
- |
|
| 149 |
+ case <-commitDebounceTimeout: |
|
| 150 |
+ schedule() |
|
| 151 |
+ commitDebounceTimer = nil |
|
| 152 |
+ commitDebounceTimeout = nil |
|
| 143 | 153 |
case <-s.stopChan: |
| 144 | 154 |
return nil |
| 145 | 155 |
} |
| ... | ... |
@@ -87,27 +87,23 @@ type Node struct {
|
| 87 | 87 |
StateDir string |
| 88 | 88 |
Error error |
| 89 | 89 |
|
| 90 |
- raftStore *raft.MemoryStorage |
|
| 91 |
- memoryStore *store.MemoryStore |
|
| 92 |
- Config *raft.Config |
|
| 93 |
- opts NewNodeOptions |
|
| 94 |
- reqIDGen *idutil.Generator |
|
| 95 |
- wait *wait |
|
| 96 |
- wal *wal.WAL |
|
| 97 |
- snapshotter *snap.Snapshotter |
|
| 98 |
- wasLeader bool |
|
| 99 |
- restored bool |
|
| 100 |
- isMember uint32 |
|
| 101 |
- joinAddr string |
|
| 90 |
+ raftStore *raft.MemoryStorage |
|
| 91 |
+ memoryStore *store.MemoryStore |
|
| 92 |
+ Config *raft.Config |
|
| 93 |
+ opts NewNodeOptions |
|
| 94 |
+ reqIDGen *idutil.Generator |
|
| 95 |
+ wait *wait |
|
| 96 |
+ wal *wal.WAL |
|
| 97 |
+ snapshotter *snap.Snapshotter |
|
| 98 |
+ restored bool |
|
| 99 |
+ signalledLeadership uint32 |
|
| 100 |
+ isMember uint32 |
|
| 101 |
+ joinAddr string |
|
| 102 | 102 |
|
| 103 | 103 |
// waitProp waits for all the proposals to be terminated before |
| 104 | 104 |
// shutting down the node. |
| 105 | 105 |
waitProp sync.WaitGroup |
| 106 | 106 |
|
| 107 |
- // forceNewCluster is a special flag used to recover from disaster |
|
| 108 |
- // scenario by pointing to an existing or backed up data directory. |
|
| 109 |
- forceNewCluster bool |
|
| 110 |
- |
|
| 111 | 107 |
confState raftpb.ConfState |
| 112 | 108 |
appliedIndex uint64 |
| 113 | 109 |
snapshotIndex uint64 |
| ... | ... |
@@ -118,7 +114,7 @@ type Node struct {
|
| 118 | 118 |
doneCh chan struct{}
|
| 119 | 119 |
// removeRaftCh notifies about node deletion from raft cluster |
| 120 | 120 |
removeRaftCh chan struct{}
|
| 121 |
- removeRaftOnce sync.Once |
|
| 121 |
+ removeRaftFunc func() |
|
| 122 | 122 |
leadershipBroadcast *events.Broadcaster |
| 123 | 123 |
|
| 124 | 124 |
// used to coordinate shutdown |
| ... | ... |
@@ -192,7 +188,6 @@ func NewNode(ctx context.Context, opts NewNodeOptions) *Node {
|
| 192 | 192 |
MaxInflightMsgs: cfg.MaxInflightMsgs, |
| 193 | 193 |
Logger: cfg.Logger, |
| 194 | 194 |
}, |
| 195 |
- forceNewCluster: opts.ForceNewCluster, |
|
| 196 | 195 |
stopCh: make(chan struct{}),
|
| 197 | 196 |
doneCh: make(chan struct{}),
|
| 198 | 197 |
removeRaftCh: make(chan struct{}),
|
| ... | ... |
@@ -215,6 +210,15 @@ func NewNode(ctx context.Context, opts NewNodeOptions) *Node {
|
| 215 | 215 |
n.reqIDGen = idutil.NewGenerator(uint16(n.Config.ID), time.Now()) |
| 216 | 216 |
n.wait = newWait() |
| 217 | 217 |
|
| 218 |
+ n.removeRaftFunc = func(n *Node) func() {
|
|
| 219 |
+ var removeRaftOnce sync.Once |
|
| 220 |
+ return func() {
|
|
| 221 |
+ removeRaftOnce.Do(func() {
|
|
| 222 |
+ close(n.removeRaftCh) |
|
| 223 |
+ }) |
|
| 224 |
+ } |
|
| 225 |
+ }(n) |
|
| 226 |
+ |
|
| 218 | 227 |
return n |
| 219 | 228 |
} |
| 220 | 229 |
|
| ... | ... |
@@ -329,6 +333,8 @@ func (n *Node) Run(ctx context.Context) error {
|
| 329 | 329 |
close(n.doneCh) |
| 330 | 330 |
}() |
| 331 | 331 |
|
| 332 |
+ wasLeader := false |
|
| 333 |
+ |
|
| 332 | 334 |
for {
|
| 333 | 335 |
select {
|
| 334 | 336 |
case <-n.ticker.C(): |
| ... | ... |
@@ -348,9 +354,11 @@ func (n *Node) Run(ctx context.Context) error {
|
| 348 | 348 |
n.Config.Logger.Error(err) |
| 349 | 349 |
} |
| 350 | 350 |
|
| 351 |
- // Send raft messages to peers |
|
| 352 |
- if err := n.send(rd.Messages); err != nil {
|
|
| 353 |
- n.Config.Logger.Error(err) |
|
| 351 |
+ if len(rd.Messages) != 0 {
|
|
| 352 |
+ // Send raft messages to peers |
|
| 353 |
+ if err := n.send(rd.Messages); err != nil {
|
|
| 354 |
+ n.Config.Logger.Error(err) |
|
| 355 |
+ } |
|
| 354 | 356 |
} |
| 355 | 357 |
|
| 356 | 358 |
// Apply snapshot to memory store. The snapshot |
| ... | ... |
@@ -358,7 +366,7 @@ func (n *Node) Run(ctx context.Context) error {
|
| 358 | 358 |
// saveToStorage. |
| 359 | 359 |
if !raft.IsEmptySnap(rd.Snapshot) {
|
| 360 | 360 |
// Load the snapshot data into the store |
| 361 |
- if err := n.restoreFromSnapshot(rd.Snapshot.Data, n.forceNewCluster); err != nil {
|
|
| 361 |
+ if err := n.restoreFromSnapshot(rd.Snapshot.Data, false); err != nil {
|
|
| 362 | 362 |
n.Config.Logger.Error(err) |
| 363 | 363 |
} |
| 364 | 364 |
n.appliedIndex = rd.Snapshot.Metadata.Index |
| ... | ... |
@@ -387,12 +395,23 @@ func (n *Node) Run(ctx context.Context) error {
|
| 387 | 387 |
// if that happens we will apply them as any |
| 388 | 388 |
// follower would. |
| 389 | 389 |
if rd.SoftState != nil {
|
| 390 |
- if n.wasLeader && rd.SoftState.RaftState != raft.StateLeader {
|
|
| 391 |
- n.wasLeader = false |
|
| 390 |
+ if wasLeader && rd.SoftState.RaftState != raft.StateLeader {
|
|
| 391 |
+ wasLeader = false |
|
| 392 | 392 |
n.wait.cancelAll() |
| 393 |
- n.leadershipBroadcast.Write(IsFollower) |
|
| 394 |
- } else if !n.wasLeader && rd.SoftState.RaftState == raft.StateLeader {
|
|
| 395 |
- n.wasLeader = true |
|
| 393 |
+ if atomic.LoadUint32(&n.signalledLeadership) == 1 {
|
|
| 394 |
+ atomic.StoreUint32(&n.signalledLeadership, 0) |
|
| 395 |
+ n.leadershipBroadcast.Write(IsFollower) |
|
| 396 |
+ } |
|
| 397 |
+ } else if !wasLeader && rd.SoftState.RaftState == raft.StateLeader {
|
|
| 398 |
+ wasLeader = true |
|
| 399 |
+ } |
|
| 400 |
+ } |
|
| 401 |
+ |
|
| 402 |
+ if wasLeader && atomic.LoadUint32(&n.signalledLeadership) != 1 {
|
|
| 403 |
+ // If all the entries in the log have become |
|
| 404 |
+ // committed, broadcast our leadership status. |
|
| 405 |
+ if n.caughtUp() {
|
|
| 406 |
+ atomic.StoreUint32(&n.signalledLeadership, 1) |
|
| 396 | 407 |
n.leadershipBroadcast.Write(IsLeader) |
| 397 | 408 |
} |
| 398 | 409 |
} |
| ... | ... |
@@ -451,17 +470,6 @@ func (n *Node) Shutdown() {
|
| 451 | 451 |
} |
| 452 | 452 |
} |
| 453 | 453 |
|
| 454 |
-// isShutdown indicates if node was shut down. |
|
| 455 |
-// This method should be called under n.stopMu to avoid races with n.stop(). |
|
| 456 |
-func (n *Node) isShutdown() bool {
|
|
| 457 |
- select {
|
|
| 458 |
- case <-n.Ctx.Done(): |
|
| 459 |
- return true |
|
| 460 |
- default: |
|
| 461 |
- return false |
|
| 462 |
- } |
|
| 463 |
-} |
|
| 464 |
- |
|
| 465 | 454 |
func (n *Node) stop() {
|
| 466 | 455 |
n.stopMu.Lock() |
| 467 | 456 |
defer n.stopMu.Unlock() |
| ... | ... |
@@ -476,16 +484,18 @@ func (n *Node) stop() {
|
| 476 | 476 |
_ = member.Conn.Close() |
| 477 | 477 |
} |
| 478 | 478 |
} |
| 479 |
+ |
|
| 479 | 480 |
n.Stop() |
| 480 | 481 |
n.ticker.Stop() |
| 481 | 482 |
if err := n.wal.Close(); err != nil {
|
| 482 | 483 |
n.Config.Logger.Errorf("raft: error closing WAL: %v", err)
|
| 483 | 484 |
} |
| 485 |
+ atomic.StoreUint32(&n.isMember, 0) |
|
| 484 | 486 |
// TODO(stevvooe): Handle ctx.Done() |
| 485 | 487 |
} |
| 486 | 488 |
|
| 487 |
-// IsLeader checks if we are the leader or not |
|
| 488 |
-func (n *Node) IsLeader() bool {
|
|
| 489 |
+// isLeader checks if we are the leader or not, without the protection of lock |
|
| 490 |
+func (n *Node) isLeader() bool {
|
|
| 489 | 491 |
if !n.IsMember() {
|
| 490 | 492 |
return false |
| 491 | 493 |
} |
| ... | ... |
@@ -496,14 +506,43 @@ func (n *Node) IsLeader() bool {
|
| 496 | 496 |
return false |
| 497 | 497 |
} |
| 498 | 498 |
|
| 499 |
-// Leader returns the id of the leader |
|
| 500 |
-func (n *Node) Leader() uint64 {
|
|
| 499 |
+// IsLeader checks if we are the leader or not, with the protection of lock |
|
| 500 |
+func (n *Node) IsLeader() bool {
|
|
| 501 |
+ n.stopMu.RLock() |
|
| 502 |
+ defer n.stopMu.RUnlock() |
|
| 503 |
+ |
|
| 504 |
+ return n.isLeader() |
|
| 505 |
+} |
|
| 506 |
+ |
|
| 507 |
+// leader returns the id of the leader, without the protection of lock |
|
| 508 |
+func (n *Node) leader() uint64 {
|
|
| 501 | 509 |
if !n.IsMember() {
|
| 502 | 510 |
return 0 |
| 503 | 511 |
} |
| 504 | 512 |
return n.Node.Status().Lead |
| 505 | 513 |
} |
| 506 | 514 |
|
| 515 |
+// Leader returns the id of the leader, with the protection of lock |
|
| 516 |
+func (n *Node) Leader() uint64 {
|
|
| 517 |
+ n.stopMu.RLock() |
|
| 518 |
+ defer n.stopMu.RUnlock() |
|
| 519 |
+ |
|
| 520 |
+ return n.leader() |
|
| 521 |
+} |
|
| 522 |
+ |
|
| 523 |
+// ReadyForProposals returns true if the node has broadcasted a message |
|
| 524 |
+// saying that it has become the leader. This means it is ready to accept |
|
| 525 |
+// proposals. |
|
| 526 |
+func (n *Node) ReadyForProposals() bool {
|
|
| 527 |
+ return atomic.LoadUint32(&n.signalledLeadership) == 1 |
|
| 528 |
+} |
|
| 529 |
+ |
|
| 530 |
+func (n *Node) caughtUp() bool {
|
|
| 531 |
+ // obnoxious function that always returns a nil error |
|
| 532 |
+ lastIndex, _ := n.raftStore.LastIndex() |
|
| 533 |
+ return n.appliedIndex >= lastIndex |
|
| 534 |
+} |
|
| 535 |
+ |
|
| 507 | 536 |
// Join asks to a member of the raft to propose |
| 508 | 537 |
// a configuration change and add us as a member thus |
| 509 | 538 |
// beginning the log replication process. This method |
| ... | ... |
@@ -534,12 +573,7 @@ func (n *Node) Join(ctx context.Context, req *api.JoinRequest) (*api.JoinRespons |
| 534 | 534 |
return nil, ErrNoRaftMember |
| 535 | 535 |
} |
| 536 | 536 |
|
| 537 |
- if n.IsStopped() {
|
|
| 538 |
- log.WithError(ErrStopped).Errorf(ErrStopped.Error()) |
|
| 539 |
- return nil, ErrStopped |
|
| 540 |
- } |
|
| 541 |
- |
|
| 542 |
- if !n.IsLeader() {
|
|
| 537 |
+ if !n.isLeader() {
|
|
| 543 | 538 |
return nil, ErrLostLeadership |
| 544 | 539 |
} |
| 545 | 540 |
|
| ... | ... |
@@ -670,11 +704,7 @@ func (n *Node) Leave(ctx context.Context, req *api.LeaveRequest) (*api.LeaveResp |
| 670 | 670 |
return nil, ErrNoRaftMember |
| 671 | 671 |
} |
| 672 | 672 |
|
| 673 |
- if n.IsStopped() {
|
|
| 674 |
- return nil, ErrStopped |
|
| 675 |
- } |
|
| 676 |
- |
|
| 677 |
- if !n.IsLeader() {
|
|
| 673 |
+ if !n.isLeader() {
|
|
| 678 | 674 |
return nil, ErrLostLeadership |
| 679 | 675 |
} |
| 680 | 676 |
|
| ... | ... |
@@ -717,12 +747,24 @@ func (n *Node) RemoveMember(ctx context.Context, id uint64) error {
|
| 717 | 717 |
// raft state machine with the provided message on the |
| 718 | 718 |
// receiving node |
| 719 | 719 |
func (n *Node) ProcessRaftMessage(ctx context.Context, msg *api.ProcessRaftMessageRequest) (*api.ProcessRaftMessageResponse, error) {
|
| 720 |
+ if msg == nil || msg.Message == nil {
|
|
| 721 |
+ return nil, grpc.Errorf(codes.InvalidArgument, "no message provided") |
|
| 722 |
+ } |
|
| 723 |
+ |
|
| 720 | 724 |
// Don't process the message if this comes from |
| 721 | 725 |
// a node in the remove set |
| 722 | 726 |
if n.cluster.IsIDRemoved(msg.Message.From) {
|
| 723 | 727 |
return nil, ErrMemberRemoved |
| 724 | 728 |
} |
| 725 | 729 |
|
| 730 |
+ if msg.Message.Type == raftpb.MsgProp {
|
|
| 731 |
+ // We don't accepted forwarded proposals. Our |
|
| 732 |
+ // current architecture depends on only the leader |
|
| 733 |
+ // making proposals, so in-flight proposals can be |
|
| 734 |
+ // guaranteed not to conflict. |
|
| 735 |
+ return nil, grpc.Errorf(codes.InvalidArgument, "proposals not accepted") |
|
| 736 |
+ } |
|
| 737 |
+ |
|
| 726 | 738 |
// can't stop the raft node while an async RPC is in progress |
| 727 | 739 |
n.stopMu.RLock() |
| 728 | 740 |
defer n.stopMu.RUnlock() |
| ... | ... |
@@ -731,10 +773,6 @@ func (n *Node) ProcessRaftMessage(ctx context.Context, msg *api.ProcessRaftMessa |
| 731 | 731 |
return nil, ErrNoRaftMember |
| 732 | 732 |
} |
| 733 | 733 |
|
| 734 |
- if n.IsStopped() {
|
|
| 735 |
- return nil, ErrStopped |
|
| 736 |
- } |
|
| 737 |
- |
|
| 738 | 734 |
if err := n.Step(n.Ctx, *msg.Message); err != nil {
|
| 739 | 735 |
return nil, err |
| 740 | 736 |
} |
| ... | ... |
@@ -772,21 +810,16 @@ func (n *Node) ResolveAddress(ctx context.Context, msg *api.ResolveAddressReques |
| 772 | 772 |
// LeaderAddr returns address of current cluster leader. |
| 773 | 773 |
// With this method Node satisfies raftpicker.AddrSelector interface. |
| 774 | 774 |
func (n *Node) LeaderAddr() (string, error) {
|
| 775 |
- n.stopMu.RLock() |
|
| 776 |
- defer n.stopMu.RUnlock() |
|
| 777 |
- if n.isShutdown() {
|
|
| 778 |
- return "", fmt.Errorf("raft node is shut down")
|
|
| 779 |
- } |
|
| 780 | 775 |
ctx, cancel := context.WithTimeout(n.Ctx, 10*time.Second) |
| 781 | 776 |
defer cancel() |
| 782 | 777 |
if err := WaitForLeader(ctx, n); err != nil {
|
| 783 | 778 |
return "", ErrNoClusterLeader |
| 784 | 779 |
} |
| 785 |
- if n.IsStopped() {
|
|
| 786 |
- return "", ErrStopped |
|
| 780 |
+ if !n.IsMember() {
|
|
| 781 |
+ return "", ErrNoRaftMember |
|
| 787 | 782 |
} |
| 788 | 783 |
ms := n.cluster.Members() |
| 789 |
- l := ms[n.Leader()] |
|
| 784 |
+ l := ms[n.leader()] |
|
| 790 | 785 |
if l == nil {
|
| 791 | 786 |
return "", ErrNoClusterLeader |
| 792 | 787 |
} |
| ... | ... |
@@ -864,6 +897,13 @@ func (n *Node) ProposeValue(ctx context.Context, storeAction []*api.StoreAction, |
| 864 | 864 |
|
| 865 | 865 |
// GetVersion returns the sequence information for the current raft round. |
| 866 | 866 |
func (n *Node) GetVersion() *api.Version {
|
| 867 |
+ n.stopMu.RLock() |
|
| 868 |
+ defer n.stopMu.RUnlock() |
|
| 869 |
+ |
|
| 870 |
+ if !n.IsMember() {
|
|
| 871 |
+ return nil |
|
| 872 |
+ } |
|
| 873 |
+ |
|
| 867 | 874 |
status := n.Node.Status() |
| 868 | 875 |
return &api.Version{Index: status.Commit}
|
| 869 | 876 |
} |
| ... | ... |
@@ -921,14 +961,6 @@ func (n *Node) IsMember() bool {
|
| 921 | 921 |
return atomic.LoadUint32(&n.isMember) == 1 |
| 922 | 922 |
} |
| 923 | 923 |
|
| 924 |
-// IsStopped checks if the raft node is stopped or not |
|
| 925 |
-func (n *Node) IsStopped() bool {
|
|
| 926 |
- if n.Node == nil {
|
|
| 927 |
- return true |
|
| 928 |
- } |
|
| 929 |
- return false |
|
| 930 |
-} |
|
| 931 |
- |
|
| 932 | 924 |
// canSubmitProposal defines if any more proposals |
| 933 | 925 |
// could be submitted and processed. |
| 934 | 926 |
func (n *Node) canSubmitProposal() bool {
|
| ... | ... |
@@ -980,6 +1012,14 @@ func (n *Node) send(messages []raftpb.Message) error {
|
| 980 | 980 |
continue |
| 981 | 981 |
} |
| 982 | 982 |
|
| 983 |
+ if m.Type == raftpb.MsgProp {
|
|
| 984 |
+ // We don't forward proposals to the leader. Our |
|
| 985 |
+ // current architecture depends on only the leader |
|
| 986 |
+ // making proposals, so in-flight proposals can be |
|
| 987 |
+ // guaranteed not to conflict. |
|
| 988 |
+ continue |
|
| 989 |
+ } |
|
| 990 |
+ |
|
| 983 | 991 |
n.asyncTasks.Add(1) |
| 984 | 992 |
go n.sendToMember(members, m) |
| 985 | 993 |
} |
| ... | ... |
@@ -1044,15 +1084,14 @@ func (n *Node) sendToMember(members map[uint64]*membership.Member, m raftpb.Mess |
| 1044 | 1044 |
_, err := conn.ProcessRaftMessage(ctx, &api.ProcessRaftMessageRequest{Message: &m})
|
| 1045 | 1045 |
if err != nil {
|
| 1046 | 1046 |
if grpc.ErrorDesc(err) == ErrMemberRemoved.Error() {
|
| 1047 |
- n.removeRaftOnce.Do(func() {
|
|
| 1048 |
- close(n.removeRaftCh) |
|
| 1049 |
- }) |
|
| 1047 |
+ n.removeRaftFunc() |
|
| 1050 | 1048 |
} |
| 1051 | 1049 |
if m.Type == raftpb.MsgSnap {
|
| 1052 | 1050 |
n.ReportSnapshot(m.To, raft.SnapshotFailure) |
| 1053 | 1051 |
} |
| 1054 |
- if n.IsStopped() {
|
|
| 1055 |
- panic("node is nil")
|
|
| 1052 |
+ if !n.IsMember() {
|
|
| 1053 |
+ // node is removed from cluster or stopped |
|
| 1054 |
+ return |
|
| 1056 | 1055 |
} |
| 1057 | 1056 |
n.ReportUnreachable(m.To) |
| 1058 | 1057 |
|
| ... | ... |
@@ -1091,7 +1130,7 @@ func (n *Node) processInternalRaftRequest(ctx context.Context, r *api.InternalRa |
| 1091 | 1091 |
ch := n.wait.register(r.ID, cb) |
| 1092 | 1092 |
|
| 1093 | 1093 |
// Do this check after calling register to avoid a race. |
| 1094 |
- if !n.IsLeader() {
|
|
| 1094 |
+ if atomic.LoadUint32(&n.signalledLeadership) != 1 {
|
|
| 1095 | 1095 |
n.wait.cancel(r.ID) |
| 1096 | 1096 |
return nil, ErrLostLeadership |
| 1097 | 1097 |
} |
| ... | ... |
@@ -1262,13 +1301,15 @@ func (n *Node) applyRemoveNode(cc raftpb.ConfChange) (err error) {
|
| 1262 | 1262 |
// a follower and the leader steps down, Campaign |
| 1263 | 1263 |
// to be the leader. |
| 1264 | 1264 |
|
| 1265 |
- if cc.NodeID == n.Leader() && !n.IsLeader() {
|
|
| 1265 |
+ if cc.NodeID == n.leader() && !n.isLeader() {
|
|
| 1266 | 1266 |
if err = n.Campaign(n.Ctx); err != nil {
|
| 1267 | 1267 |
return err |
| 1268 | 1268 |
} |
| 1269 | 1269 |
} |
| 1270 | 1270 |
|
| 1271 | 1271 |
if cc.NodeID == n.Config.ID {
|
| 1272 |
+ n.removeRaftFunc() |
|
| 1273 |
+ |
|
| 1272 | 1274 |
// wait the commit ack to be sent before closing connection |
| 1273 | 1275 |
n.asyncTasks.Wait() |
| 1274 | 1276 |
|
| ... | ... |
@@ -185,7 +185,7 @@ func clip(x float64) float64 {
|
| 185 | 185 |
func (mwr *remotesWeightedRandom) observe(peer api.Peer, weight float64) {
|
| 186 | 186 |
|
| 187 | 187 |
// While we have a decent, ad-hoc approach here to weight subsequent |
| 188 |
- // observerations, we may want to look into applying forward decay: |
|
| 188 |
+ // observations, we may want to look into applying forward decay: |
|
| 189 | 189 |
// |
| 190 | 190 |
// http://dimacs.rutgers.edu/~graham/pubs/papers/fwddecay.pdf |
| 191 | 191 |
// |