mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
bd5ceb5142
Signed-off-by: Anders Eknert <anders@styra.com>
110 lines
2.1 KiB
Go
110 lines
2.1 KiB
Go
// Copyright 2024 The OPA Authors. All rights reserved.
|
|
// Use of this source code is governed by an Apache2
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package debug
|
|
|
|
import (
|
|
"github.com/open-policy-agent/opa/v1/rego"
|
|
"github.com/open-policy-agent/opa/v1/topdown"
|
|
)
|
|
|
|
type stack interface {
|
|
topdown.QueryTracer
|
|
Current() (int, *topdown.Event)
|
|
Event(i int) *topdown.Event
|
|
Next() (int, *topdown.Event)
|
|
Result() rego.ResultSet
|
|
Close() error
|
|
}
|
|
|
|
type debugTracer struct {
|
|
history []*topdown.Event
|
|
eventChan chan *topdown.Event
|
|
waitChan chan bool
|
|
enabled bool
|
|
resultSet rego.ResultSet
|
|
}
|
|
|
|
func newDebugTracer() *debugTracer {
|
|
return &debugTracer{
|
|
eventChan: make(chan *topdown.Event),
|
|
waitChan: make(chan bool),
|
|
enabled: true,
|
|
}
|
|
}
|
|
|
|
func (dt *debugTracer) Enabled() bool {
|
|
return dt.enabled
|
|
}
|
|
|
|
func (dt *debugTracer) TraceEvent(e topdown.Event) {
|
|
if !dt.enabled {
|
|
return
|
|
}
|
|
|
|
defer func() {
|
|
if recover() != nil {
|
|
dt.enabled = false
|
|
}
|
|
}()
|
|
|
|
dt.eventChan <- &e
|
|
// Block until the consumer wants another event, so that evaluation isn't "ahead"
|
|
<-dt.waitChan
|
|
}
|
|
|
|
func (*debugTracer) Config() topdown.TraceConfig {
|
|
return topdown.TraceConfig{
|
|
PlugLocalVars: true,
|
|
}
|
|
}
|
|
|
|
func (dt *debugTracer) Current() (int, *topdown.Event) {
|
|
stackLength := len(dt.history)
|
|
if stackLength > 0 {
|
|
return stackLength - 1, dt.history[len(dt.history)-1]
|
|
}
|
|
return -1, nil
|
|
}
|
|
|
|
func (dt *debugTracer) Event(i int) *topdown.Event {
|
|
if i >= 0 && i < len(dt.history) {
|
|
return dt.history[i]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (dt *debugTracer) Next() (int, *topdown.Event) {
|
|
if !dt.enabled {
|
|
return -1, nil
|
|
}
|
|
|
|
if len(dt.history) > 0 {
|
|
// We don't need to unblock the producer for the first event.
|
|
dt.waitChan <- true
|
|
}
|
|
|
|
e, ok := <-dt.eventChan
|
|
if ok {
|
|
dt.history = append(dt.history, e)
|
|
return len(dt.history) - 1, e
|
|
}
|
|
return len(dt.history) - 1, nil
|
|
}
|
|
|
|
func (dt *debugTracer) Result() rego.ResultSet {
|
|
return dt.resultSet
|
|
}
|
|
|
|
func (dt *debugTracer) Close() error {
|
|
if !dt.enabled {
|
|
return nil
|
|
}
|
|
|
|
dt.enabled = false
|
|
close(dt.eventChan)
|
|
close(dt.waitChan)
|
|
return nil
|
|
}
|