mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
7e5c14f255
These changes update the storage to accept arbitrary key/value parameters when creating transactions. The container is passed to trigger callbacks that are invoked when transactions commit. The use case for this is avoiding parse and compile operations on modules loaded out of bundles. The parse and compile step on large bundles can take several seconds. Since the triggers are executed while the store's read-lock is held, policies queries block. Signed-off-by: Torin Sandall <torinsandall@gmail.com>
220 lines
6.8 KiB
Go
220 lines
6.8 KiB
Go
// Copyright 2016 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 storage
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/open-policy-agent/opa/ast"
|
|
)
|
|
|
|
// Transaction defines the interface that identifies a consistent snapshot over
|
|
// the policy engine's storage layer.
|
|
type Transaction interface {
|
|
ID() uint64
|
|
}
|
|
|
|
// Store defines the interface for the storage layer's backend.
|
|
type Store interface {
|
|
Trigger
|
|
Policy
|
|
Indexing
|
|
|
|
// NewTransaction is called create a new transaction in the store.
|
|
NewTransaction(ctx context.Context, params ...TransactionParams) (Transaction, error)
|
|
|
|
// Read is called to fetch a document referred to by path.
|
|
Read(ctx context.Context, txn Transaction, path Path) (interface{}, error)
|
|
|
|
// Write is called to modify a document referred to by path.
|
|
Write(ctx context.Context, txn Transaction, op PatchOp, path Path, value interface{}) error
|
|
|
|
// Commit is called to finish the transaction. If Commit returns an error, the
|
|
// transaction must be automatically aborted by the Store implementation.
|
|
Commit(ctx context.Context, txn Transaction) error
|
|
|
|
// Abort is called to cancel the transaction.
|
|
Abort(ctx context.Context, txn Transaction)
|
|
}
|
|
|
|
// TransactionParams describes a new transaction.
|
|
type TransactionParams struct {
|
|
|
|
// Write indicates if this transaction will perform any write operations.
|
|
Write bool
|
|
|
|
// Context contains key/value pairs passed to triggers.
|
|
Context *Context
|
|
}
|
|
|
|
// Context is a simple container for key/value pairs.
|
|
type Context struct {
|
|
values map[interface{}]interface{}
|
|
}
|
|
|
|
// NewContext returns a new context object.
|
|
func NewContext() *Context {
|
|
return &Context{
|
|
values: map[interface{}]interface{}{},
|
|
}
|
|
}
|
|
|
|
// Get returns the key value in the context.
|
|
func (ctx *Context) Get(key interface{}) interface{} {
|
|
if ctx == nil {
|
|
return nil
|
|
}
|
|
return ctx.values[key]
|
|
}
|
|
|
|
// Put adds a key/value pair to the context.
|
|
func (ctx *Context) Put(key, value interface{}) {
|
|
ctx.values[key] = value
|
|
}
|
|
|
|
// WriteParams specifies the TransactionParams for a write transaction.
|
|
var WriteParams = TransactionParams{
|
|
Write: true,
|
|
}
|
|
|
|
// PatchOp is the enumeration of supposed modifications.
|
|
type PatchOp int
|
|
|
|
// Patch supports add, remove, and replace operations.
|
|
const (
|
|
AddOp PatchOp = iota
|
|
RemoveOp = iota
|
|
ReplaceOp = iota
|
|
)
|
|
|
|
// WritesNotSupported provides a default implementation of the write
|
|
// interface which may be used if the backend does not support writes.
|
|
type WritesNotSupported struct{}
|
|
|
|
func (WritesNotSupported) Write(ctx context.Context, txn Transaction, op PatchOp, path Path, value interface{}) error {
|
|
return writesNotSupportedError()
|
|
}
|
|
|
|
// Policy defines the interface for policy module storage.
|
|
type Policy interface {
|
|
ListPolicies(context.Context, Transaction) ([]string, error)
|
|
GetPolicy(context.Context, Transaction, string) ([]byte, error)
|
|
UpsertPolicy(context.Context, Transaction, string, []byte) error
|
|
DeletePolicy(context.Context, Transaction, string) error
|
|
}
|
|
|
|
// PolicyNotSupported provides a default implementation of the policy interface
|
|
// which may be used if the backend does not support policy storage.
|
|
type PolicyNotSupported struct{}
|
|
|
|
// ListPolicies always returns a PolicyNotSupportedErr.
|
|
func (PolicyNotSupported) ListPolicies(context.Context, Transaction) ([]string, error) {
|
|
return nil, policyNotSupportedError()
|
|
}
|
|
|
|
// GetPolicy always returns a PolicyNotSupportedErr.
|
|
func (PolicyNotSupported) GetPolicy(context.Context, Transaction, string) ([]byte, error) {
|
|
return nil, policyNotSupportedError()
|
|
}
|
|
|
|
// UpsertPolicy always returns a PolicyNotSupportedErr.
|
|
func (PolicyNotSupported) UpsertPolicy(context.Context, Transaction, string, []byte) error {
|
|
return policyNotSupportedError()
|
|
}
|
|
|
|
// DeletePolicy always returns a PolicyNotSupportedErr.
|
|
func (PolicyNotSupported) DeletePolicy(context.Context, Transaction, string) error {
|
|
return policyNotSupportedError()
|
|
}
|
|
|
|
// PolicyEvent describes a change to a policy.
|
|
type PolicyEvent struct {
|
|
ID string
|
|
Data []byte
|
|
Removed bool
|
|
}
|
|
|
|
// DataEvent describes a change to a base data document.
|
|
type DataEvent struct {
|
|
Path Path
|
|
Data interface{}
|
|
Removed bool
|
|
}
|
|
|
|
// TriggerEvent describes the changes that caused the trigger to be invoked.
|
|
type TriggerEvent struct {
|
|
Policy []PolicyEvent
|
|
Data []DataEvent
|
|
Context *Context
|
|
}
|
|
|
|
// IsZero returns true if the TriggerEvent indicates no changes occurred. This
|
|
// function is primarily for test purposes.
|
|
func (e TriggerEvent) IsZero() bool {
|
|
return !e.PolicyChanged() && !e.DataChanged()
|
|
}
|
|
|
|
// PolicyChanged returns true if the trigger was caused by a policy change.
|
|
func (e TriggerEvent) PolicyChanged() bool {
|
|
return len(e.Policy) > 0
|
|
}
|
|
|
|
// DataChanged returns true if the trigger was caused by a data change.
|
|
func (e TriggerEvent) DataChanged() bool {
|
|
return len(e.Data) > 0
|
|
}
|
|
|
|
// TriggerConfig contains the trigger registration configuration.
|
|
type TriggerConfig struct {
|
|
|
|
// OnCommit is invoked when a transaction is successfully committed. The
|
|
// callback is invoked with a handle to the write transaction that
|
|
// successfully committed before other clients see the changes.
|
|
OnCommit func(ctx context.Context, txn Transaction, event TriggerEvent)
|
|
}
|
|
|
|
// Trigger defines the interface that stores implement to register for change
|
|
// notifications when the store is changed.
|
|
type Trigger interface {
|
|
Register(ctx context.Context, txn Transaction, config TriggerConfig) (TriggerHandle, error)
|
|
}
|
|
|
|
// TriggersNotSupported provides default implementations of the Trigger
|
|
// interface which may be used if the backend does not support triggers.
|
|
type TriggersNotSupported struct{}
|
|
|
|
// Register always returns an error indicating triggers are not supported.
|
|
func (TriggersNotSupported) Register(context.Context, Transaction, TriggerConfig) (TriggerHandle, error) {
|
|
return nil, triggersNotSupportedError()
|
|
}
|
|
|
|
// TriggerHandle defines the interface that can be used to unregister triggers that have
|
|
// been registered on a Store.
|
|
type TriggerHandle interface {
|
|
Unregister(ctx context.Context, txn Transaction)
|
|
}
|
|
|
|
// IndexIterator defines the interface for iterating over index results.
|
|
type IndexIterator func(*ast.ValueMap) error
|
|
|
|
// Indexing defines the interface for building an index.
|
|
type Indexing interface {
|
|
Build(ctx context.Context, txn Transaction, ref ast.Ref) (Index, error)
|
|
}
|
|
|
|
// Index defines the interface for searching a pre-built index.
|
|
type Index interface {
|
|
Lookup(ctx context.Context, txn Transaction, value interface{}, iter IndexIterator) error
|
|
}
|
|
|
|
// IndexingNotSupported provides default implementations of the Indexing
|
|
// interface which may be used if the backend does not support indexing.
|
|
type IndexingNotSupported struct{}
|
|
|
|
// Build always returns an error indicating indexing is not supported.
|
|
func (IndexingNotSupported) Build(context.Context, Transaction, ast.Ref) (Index, error) {
|
|
return nil, indexingNotSupportedError()
|
|
}
|