As part of my effort to better understand OPA I fired up Delve to understand how different CLI options got wired up. It was pretty straight forward to understand the basics but I wanted to debug the run command which needs stdin but Delve uses stdin to take its own commands.

After many failed attempts and searching I stumbled onto a post by Laurent Stacul which covered this case. I’m documenting it here as well for personal reference.

Terminal 1 Link to heading

A headless run of the project needs to be started in the first terminal. If --listen isn’t provided a random port is assigned so we provide a static port for ease of use.

~/opa > dlv debug --headless --listen :4747 . -- run 
API server listening at: [::]:4747

Terminal 2 Link to heading

In the second terminal you connect to the headless process from the first. From there we can set a break point and then resume execution.

~/opa > dlv connect :4747
Type 'help' for list of commands.
(dlv) break bar opa/runtime/runtime.go:470
Breakpoint bar set at 0xf7cdb2 for github.com/open-policy-agent/opa/runtime.(*Runtime).StartREPL() ./opa/runtime/runtime.go:47
(dlv) continue
> [bar] github.com/open-policy-agent/opa/runtime.(*Runtime).StartREPL() ./opa/runtime/runtime.go:470 (hits goroutine(1):1 total:1) (PC: 0xf7cdb2)
   465: }
   466:
   467: // StartREPL starts the runtime in REPL mode. This function will block the calling goroutine.
   468: func (rt *Runtime) StartREPL(ctx context.Context) {
   469:
=> 470:         if err := rt.Manager.Start(ctx); err != nil {
   471:                 fmt.Fprintln(rt.Params.Output, "error starting plugins:", err)
   472:                 os.Exit(1)
   473:         }
   474:
   475:         defer rt.Manager.Stop(ctx)