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 }