Commit 6c25f371 authored by James DeFelice's avatar James DeFelice

update to latest mesos-go for master failover improvements

parent 9ceed60b
......@@ -757,43 +757,43 @@
},
{
"ImportPath": "github.com/mesos/mesos-go/auth",
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
"Comment": "before-0.26-protos-29-gb755e34",
"Rev": "b755e34cdb14a6de15ccb50605786c9ecacbfc1f"
},
{
"ImportPath": "github.com/mesos/mesos-go/detector",
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
"Comment": "before-0.26-protos-29-gb755e34",
"Rev": "b755e34cdb14a6de15ccb50605786c9ecacbfc1f"
},
{
"ImportPath": "github.com/mesos/mesos-go/executor",
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
"Comment": "before-0.26-protos-29-gb755e34",
"Rev": "b755e34cdb14a6de15ccb50605786c9ecacbfc1f"
},
{
"ImportPath": "github.com/mesos/mesos-go/mesosproto",
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
"Comment": "before-0.26-protos-29-gb755e34",
"Rev": "b755e34cdb14a6de15ccb50605786c9ecacbfc1f"
},
{
"ImportPath": "github.com/mesos/mesos-go/mesosutil",
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
"Comment": "before-0.26-protos-29-gb755e34",
"Rev": "b755e34cdb14a6de15ccb50605786c9ecacbfc1f"
},
{
"ImportPath": "github.com/mesos/mesos-go/messenger",
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
"Comment": "before-0.26-protos-29-gb755e34",
"Rev": "b755e34cdb14a6de15ccb50605786c9ecacbfc1f"
},
{
"ImportPath": "github.com/mesos/mesos-go/scheduler",
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
"Comment": "before-0.26-protos-29-gb755e34",
"Rev": "b755e34cdb14a6de15ccb50605786c9ecacbfc1f"
},
{
"ImportPath": "github.com/mesos/mesos-go/upid",
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
"Comment": "before-0.26-protos-29-gb755e34",
"Rev": "b755e34cdb14a6de15ccb50605786c9ecacbfc1f"
},
{
"ImportPath": "github.com/miekg/dns",
......
......@@ -3,6 +3,7 @@ package auth
import (
"errors"
"fmt"
"time"
"github.com/mesos/mesos-go/auth/callback"
"github.com/mesos/mesos-go/upid"
......@@ -38,8 +39,14 @@ func Login(ctx context.Context, handler callback.Handler) error {
type loginKeyType int
const (
loginProviderNameKey loginKeyType = iota // name of login provider to use
parentUpidKey // upid.UPID of some parent process
// name of login provider to use
loginProviderNameKey loginKeyType = iota
// upid.UPID of some parent process
parentUpidKey
// time.Duration that limits the overall duration of an auth attempt
timeoutKey
)
// Return a context that inherits all values from the parent ctx and specifies
......@@ -74,7 +81,20 @@ func ParentUPIDFrom(ctx context.Context) (pid upid.UPID, ok bool) {
func ParentUPID(ctx context.Context) (upid *upid.UPID) {
if upid, ok := ParentUPIDFrom(ctx); ok {
return &upid
} else {
return nil
}
return nil
}
func TimeoutFrom(ctx context.Context) (d time.Duration, ok bool) {
d, ok = ctx.Value(timeoutKey).(time.Duration)
return
}
func Timeout(ctx context.Context) (d time.Duration) {
d, _ = TimeoutFrom(ctx)
return
}
func WithTimeout(ctx context.Context, d time.Duration) context.Context {
return context.WithValue(ctx, timeoutKey, d)
}
......@@ -22,6 +22,7 @@ It has these top-level messages:
InternalMasterChangeDetected
InternalTryAuthentication
InternalAuthenticationResult
InternalNetworkError
FrameworkID
OfferID
SlaveID
......
......@@ -75,3 +75,29 @@ func (m *InternalAuthenticationResult) GetPid() string {
}
return ""
}
type InternalNetworkError struct {
// master pid that this event pertains to
Pid *string `protobuf:"bytes,1,req,name=pid" json:"pid,omitempty"`
// driver session UUID
Session *string `protobuf:"bytes,2,opt,name=session" json:"session,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *InternalNetworkError) Reset() { *m = InternalNetworkError{} }
func (m *InternalNetworkError) String() string { return proto.CompactTextString(m) }
func (*InternalNetworkError) ProtoMessage() {}
func (m *InternalNetworkError) GetPid() string {
if m != nil && m.Pid != nil {
return *m.Pid
}
return ""
}
func (m *InternalNetworkError) GetSession() string {
if m != nil && m.Session != nil {
return *m.Session
}
return ""
}
......@@ -21,3 +21,10 @@ message InternalAuthenticationResult {
// master pid that this result pertains to
required string pid = 3;
}
message InternalNetworkError {
// master pid that this event pertains to
required string pid = 1;
// driver session UUID
optional string session = 2;
}
......@@ -20,9 +20,6 @@ import (
)
const (
DefaultReadTimeout = 5 * time.Second
DefaultWriteTimeout = 5 * time.Second
// writeFlushPeriod is the amount of time we're willing to wait for a single
// response buffer to be fully written to the underlying TCP connection; after
// this amount of time the remaining bytes of the response are discarded. see
......@@ -43,13 +40,8 @@ func (did *decoderID) next() decoderID {
var (
errHijackFailed = errors.New("failed to hijack http connection")
did decoderID // decoder ID counter
closedChan = make(chan struct{})
)
func init() {
close(closedChan)
}
type Decoder interface {
Requests() <-chan *Request
Err() <-chan error
......@@ -99,8 +91,8 @@ func DecodeHTTP(w http.ResponseWriter, r *http.Request) Decoder {
req: r,
shouldQuit: make(chan struct{}),
forceQuit: make(chan struct{}),
readTimeout: DefaultReadTimeout,
writeTimeout: DefaultWriteTimeout,
readTimeout: ReadTimeout,
writeTimeout: WriteTimeout,
idtag: id.String(),
outCh: make(chan *bytes.Buffer),
}
......
......@@ -30,6 +30,7 @@ import (
mesos "github.com/mesos/mesos-go/mesosproto"
"github.com/mesos/mesos-go/mesosproto/scheduler"
"github.com/mesos/mesos-go/mesosutil/process"
"github.com/mesos/mesos-go/messenger/sessionid"
"github.com/mesos/mesos-go/upid"
"golang.org/x/net/context"
)
......@@ -52,16 +53,19 @@ type Messenger interface {
UPID() upid.UPID
}
type errorHandlerFunc func(context.Context, *Message, error) error
type dispatchFunc func(errorHandlerFunc)
// MesosMessenger is an implementation of the Messenger interface.
type MesosMessenger struct {
upid upid.UPID
encodingQueue chan *Message
sendingQueue chan *Message
sendingQueue chan dispatchFunc
installedMessages map[string]reflect.Type
installedHandlers map[string]MessageHandler
stop chan struct{}
stopOnce sync.Once
tr Transporter
guardHandlers sync.RWMutex // protect simultaneous changes to messages/handlers maps
}
// ForHostname creates a new default messenger (HTTP), using UPIDBindingAddress to
......@@ -137,18 +141,17 @@ func UPIDBindingAddress(hostname string, bindingAddress net.IP) (string, error)
}
// NewMesosMessenger creates a new mesos messenger.
func NewHttp(upid upid.UPID) *MesosMessenger {
return NewHttpWithBindingAddress(upid, nil)
func NewHttp(upid upid.UPID, opts ...httpOpt) *MesosMessenger {
return NewHttpWithBindingAddress(upid, nil, opts...)
}
func NewHttpWithBindingAddress(upid upid.UPID, address net.IP) *MesosMessenger {
return New(upid, NewHTTPTransporter(upid, address))
func NewHttpWithBindingAddress(upid upid.UPID, address net.IP, opts ...httpOpt) *MesosMessenger {
return New(NewHTTPTransporter(upid, address, opts...))
}
func New(upid upid.UPID, t Transporter) *MesosMessenger {
func New(t Transporter) *MesosMessenger {
return &MesosMessenger{
encodingQueue: make(chan *Message, defaultQueueSize),
sendingQueue: make(chan *Message, defaultQueueSize),
sendingQueue: make(chan dispatchFunc, defaultQueueSize),
installedMessages: make(map[string]reflect.Type),
installedHandlers: make(map[string]MessageHandler),
tr: t,
......@@ -168,6 +171,10 @@ func (m *MesosMessenger) Install(handler MessageHandler, msg proto.Message) erro
if _, ok := m.installedMessages[name]; ok {
return fmt.Errorf("Message %v is already installed", name)
}
m.guardHandlers.Lock()
defer m.guardHandlers.Unlock()
m.installedMessages[name] = mtype.Elem()
m.installedHandlers[name] = handler
m.tr.Install(name)
......@@ -184,12 +191,27 @@ func (m *MesosMessenger) Send(ctx context.Context, upid *upid.UPID, msg proto.Me
} else if *upid == m.upid {
return fmt.Errorf("Send the message to self")
}
b, err := proto.Marshal(msg)
if err != nil {
return err
}
name := getMessageName(msg)
log.V(2).Infof("Sending message %v to %v\n", name, upid)
wrapped := &Message{upid, name, msg, b}
d := dispatchFunc(func(rf errorHandlerFunc) {
err := m.tr.Send(ctx, wrapped)
err = rf(ctx, wrapped, err)
if err != nil {
m.reportError("send", wrapped, err)
}
})
select {
case <-ctx.Done():
return ctx.Err()
case m.encodingQueue <- &Message{upid, name, msg, nil}:
case m.sendingQueue <- d:
return nil
}
}
......@@ -206,14 +228,18 @@ func (m *MesosMessenger) Route(ctx context.Context, upid *upid.UPID, msg proto.M
return m.Send(ctx, upid, msg)
}
// TODO(jdef) this has an unfortunate performance impact for self-messaging. implement
// something more reasonable here.
data, err := proto.Marshal(msg)
if err != nil {
return err
}
name := getMessageName(msg)
return m.tr.Inject(ctx, &Message{upid, name, msg, data})
log.V(2).Infof("routing message %q to self", name)
_, handler, ok := m.messageBinding(name)
if !ok {
return fmt.Errorf("failed to route message, no message binding for %q", name)
}
// the implication of this is that messages can be delivered to self even if the
// messenger has been stopped. is that OK?
go handler(upid, msg)
return nil
}
// Start starts the messenger; expects to be called once and only once.
......@@ -231,7 +257,6 @@ func (m *MesosMessenger) Start() error {
m.upid = pid
go m.sendLoop()
go m.encodeLoop()
go m.decodeLoop()
// wait for a listener error or a stop signal; either way stop the messenger
......@@ -267,7 +292,7 @@ func (m *MesosMessenger) Stop() (err error) {
defer close(m.stop)
}
log.Info("stopping messenger..")
log.Infof("stopping messenger %v..", m.upid)
//TODO(jdef) don't hardcode the graceful flag here
if err2 := m.tr.Stop(true); err2 != nil && err2 != errTerminal {
......@@ -283,53 +308,14 @@ func (m *MesosMessenger) UPID() upid.UPID {
return m.upid
}
func (m *MesosMessenger) encodeLoop() {
for {
select {
case <-m.stop:
return
case msg := <-m.encodingQueue:
e := func() error {
//TODO(jdef) implement timeout for context
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
b, err := proto.Marshal(msg.ProtoMessage)
if err != nil {
return err
}
msg.Bytes = b
select {
case <-ctx.Done():
return ctx.Err()
case m.sendingQueue <- msg:
return nil
}
}()
if e != nil {
m.reportError(fmt.Errorf("Failed to enqueue message %v: %v", msg, e))
}
}
}
}
func (m *MesosMessenger) reportError(err error) {
log.V(2).Info(err)
//TODO(jdef) implement timeout for context
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
c := make(chan error, 1)
pid := m.upid
go func() { c <- m.Route(ctx, &pid, &mesos.FrameworkErrorMessage{Message: proto.String(err.Error())}) }()
select {
case <-ctx.Done():
<-c // wait for Route to return
case e := <-c:
if e != nil {
log.Errorf("failed to report error %v due to: %v", err, e)
}
func (m *MesosMessenger) reportError(action string, msg *Message, err error) {
// log message transmission errors but don't shoot the messenger.
// this approach essentially drops all undelivered messages on the floor.
name := ""
if msg != nil {
name = msg.Name
}
log.Errorf("failed to %s message %q: %+v", action, name, err)
}
func (m *MesosMessenger) sendLoop() {
......@@ -337,27 +323,28 @@ func (m *MesosMessenger) sendLoop() {
select {
case <-m.stop:
return
case msg := <-m.sendingQueue:
e := func() error {
//TODO(jdef) implement timeout for context
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
c := make(chan error, 1)
go func() { c <- m.tr.Send(ctx, msg) }()
select {
case <-ctx.Done():
// Transport layer must use the context to detect cancelled requests.
<-c // wait for Send to return
return ctx.Err()
case err := <-c:
return err
case f := <-m.sendingQueue:
f(errorHandlerFunc(func(ctx context.Context, msg *Message, err error) error {
if _, ok := err.(*networkError); ok {
// if transport reports a network error, then
// we're probably disconnected from the remote process?
pid := msg.UPID.String()
neterr := &mesos.InternalNetworkError{Pid: &pid}
sessionID, ok := sessionid.FromContext(ctx)
if ok {
neterr.Session = &sessionID
}
log.V(1).Infof("routing network error for pid %q session %q", pid, sessionID)
err2 := m.Route(ctx, &m.upid, neterr)
if err2 != nil {
log.Error(err2)
} else {
log.V(1).Infof("swallowing raw error because we're reporting a networkError: %v", err)
return nil
}
}
}()
if e != nil {
m.reportError(fmt.Errorf("Failed to send message %v: %v", msg.Name, e))
}
return err
}))
}
}
}
......@@ -379,17 +366,42 @@ func (m *MesosMessenger) decodeLoop() {
panic(fmt.Sprintf("unexpected transport error: %v", err))
}
}
log.V(2).Infof("Receiving message %v from %v\n", msg.Name, msg.UPID)
msg.ProtoMessage = reflect.New(m.installedMessages[msg.Name]).Interface().(proto.Message)
protoMessage, handler, found := m.messageBinding(msg.Name)
if !found {
log.Warningf("no message binding for message %q", msg.Name)
continue
}
msg.ProtoMessage = protoMessage
if err := proto.Unmarshal(msg.Bytes, msg.ProtoMessage); err != nil {
log.Errorf("Failed to unmarshal message %v: %v\n", msg, err)
continue
}
// TODO(yifan): Catch panic.
m.installedHandlers[msg.Name](msg.UPID, msg.ProtoMessage)
handler(msg.UPID, msg.ProtoMessage)
}
}
func (m *MesosMessenger) messageBinding(name string) (proto.Message, MessageHandler, bool) {
m.guardHandlers.RLock()
defer m.guardHandlers.RUnlock()
gotype, ok := m.installedMessages[name]
if !ok {
return nil, nil, false
}
handler, ok := m.installedHandlers[name]
if !ok {
return nil, nil, false
}
protoMessage := reflect.New(gotype).Interface().(proto.Message)
return protoMessage, handler, true
}
// getMessageName returns the name of the message in the mesos manner.
func getMessageName(msg proto.Message) string {
var msgName string
......
package sessionid
import (
"golang.org/x/net/context"
)
type key int
const sessionIDKey = 0
func NewContext(ctx context.Context, sessionID string) context.Context {
return context.WithValue(ctx, sessionIDKey, sessionID)
}
func FromContext(ctx context.Context) (string, bool) {
sessionID, ok := ctx.Value(sessionIDKey).(string)
return sessionID, ok
}
......@@ -33,11 +33,6 @@ type Transporter interface {
//Will stop receiving when transport is stopped.
Recv() (*Message, error)
//Inject injects a message to the incoming queue. Must use context to
//determine cancelled requests. Injection is aborted if the transport
//is stopped.
Inject(ctx context.Context, msg *Message) error
//Install mount an handler based on incoming message name.
Install(messageName string)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment