docs/debug/debug.go

101 lines
2.9 KiB
Go

package debug
import (
"fmt"
"io"
"log"
"github.com/docker/libswarm"
"github.com/docker/libswarm/utils"
)
// The Debug service is an example of intercepting messages between a receiver and a sender.
// The service also exposes messages passing through it for debug purposes.
func Debug() libswarm.Sender {
dbgInstance := &debug{
service: libswarm.NewServer(),
}
sender := libswarm.NewServer()
sender.OnVerb(libswarm.Spawn, libswarm.Handler(dbgInstance.spawn))
return sender
}
// Debug service type
type debug struct {
service *libswarm.Server
out libswarm.Sender
}
// Spawn will return a new instance as the Ret channel of the message sent back
func (dbg *debug) spawn(msg *libswarm.Message) (err error) {
// By sending back a task, libswarm will run the function with the in and out arguments
// set to the services present before and after this one in the pipeline.
instance := utils.Task(func(in libswarm.Receiver, out libswarm.Sender) {
// Setup our channels
dbg.out = out
// Set up the debug interceptor
dbg.service.Catchall(libswarm.Handler(dbg.catchall))
// Copy everything from the receiver to our service. By copying like this in the task
// we can use the catchall handler instead of handling the message here.
libswarm.Copy(dbg.service, in)
})
// Inform the system of our new instance
msg.Ret.Send(&libswarm.Message{
Verb: libswarm.Ack,
Ret: instance,
})
return
}
// Catches all messages sent to the service
func (dbg *debug) catchall(msg *libswarm.Message) (err error) {
log.Printf("[debug] ---> Outbound Message ---> { Verb: %s, Args: %v }\n", msg.Verb, msg.Args)
// If there's no output after us then we'll just reply with an error
// informing the receiver that the verb is not implemented.
if dbg.out == nil {
return fmt.Errorf("[debug] Verb: %s is not implemented.", msg.Verb)
}
// We forward the message with a special Ret value of "libswarm.RetPipe" - this
// asks libchan to open a new pipe so that we can read replies from upstream
forwardedMsg := &libswarm.Message{
Verb: msg.Verb,
Args: msg.Args,
Att: msg.Att,
Ret: libswarm.RetPipe,
}
// Send the forwarded message
if inbound, err := dbg.out.Send(forwardedMsg); err != nil {
return fmt.Errorf("[debug] Failed to forward msg. Reason: %v\n", err)
} else if inbound == nil {
return fmt.Errorf("[debug] Inbound channel nil.\n")
} else {
for {
// Relay all messages returned until the inbound channel is empty (EOF)
var reply *libswarm.Message
if reply, err = inbound.Receive(0); err != nil {
if err == io.EOF {
// EOF is expected
err = nil
}
break
}
// Forward the message back downstream
if _, err = msg.Ret.Send(reply); err != nil {
return fmt.Errorf("[debug] Failed to forward msg. Reason: %v\n", err)
}
log.Printf("[debug] <--- Inbound Message <--- { Verb: %s, Args: %v }\n", reply.Verb, reply.Args)
}
}
return
}