Skip to content

Commit

Permalink
Merge pull request #866 from josephschorr/caveats-flag
Browse files Browse the repository at this point in the history
Add caveats flag to disable writing by default on all datastores
  • Loading branch information
jzelinskie authored Oct 4, 2022
2 parents b0dfd00 + 3845dac commit ddce492
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 14 deletions.
6 changes: 6 additions & 0 deletions internal/datastore/memdb/caveat.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package memdb

import (
"fmt"

"github.com/authzed/spicedb/pkg/datastore"
core "github.com/authzed/spicedb/pkg/proto/core/v1"

Expand All @@ -22,6 +24,10 @@ func (c *caveat) Unwrap() *core.Caveat {
}

func (r *memdbReader) ReadCaveatByName(name string) (*core.Caveat, error) {
if !r.enableCaveats {
return nil, fmt.Errorf("caveats are not enabled")
}

r.lockOrPanic()
defer r.Unlock()

Expand Down
21 changes: 16 additions & 5 deletions internal/datastore/memdb/memdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ func NewMemdbDatastore(
watchBufferLength uint16,
revisionQuantization,
gcWindow time.Duration,
) (datastore.Datastore, error) {
return NewMemdbDatastoreWithCaveatsOption(watchBufferLength, revisionQuantization, gcWindow, true)
}

func NewMemdbDatastoreWithCaveatsOption(
watchBufferLength uint16,
revisionQuantization,
gcWindow time.Duration,
enableCaveats bool,
) (datastore.Datastore, error) {
if revisionQuantization > gcWindow {
return nil, errors.New("gc window must be larger than quantization interval")
Expand Down Expand Up @@ -70,6 +79,7 @@ func NewMemdbDatastore(
quantizationPeriod: decimal.NewFromInt(revisionQuantization.Nanoseconds()),
watchBufferLength: watchBufferLength,
uniqueID: uniqueID,
enableCaveats: enableCaveats,
}, nil
}

Expand All @@ -84,6 +94,7 @@ type memdbDatastore struct {
quantizationPeriod datastore.Revision
watchBufferLength uint16
uniqueID string
enableCaveats bool
}

type snapshot struct {
Expand All @@ -96,11 +107,11 @@ func (mdb *memdbDatastore) SnapshotReader(revision datastore.Revision) datastore
defer mdb.RUnlock()

if len(mdb.revisions) == 0 {
return &memdbReader{nil, nil, datastore.NoRevision, fmt.Errorf("memdb datastore is not ready")}
return &memdbReader{nil, nil, datastore.NoRevision, fmt.Errorf("memdb datastore is not ready"), mdb.enableCaveats}
}

if err := mdb.checkRevisionLocal(revision); err != nil {
return &memdbReader{nil, nil, datastore.NoRevision, err}
return &memdbReader{nil, nil, datastore.NoRevision, err, mdb.enableCaveats}
}

revIndex := sort.Search(len(mdb.revisions), func(i int) bool {
Expand All @@ -114,7 +125,7 @@ func (mdb *memdbDatastore) SnapshotReader(revision datastore.Revision) datastore

rev := mdb.revisions[revIndex]
if rev.db == nil {
return &memdbReader{nil, nil, datastore.NoRevision, fmt.Errorf("memdb datastore is already closed")}
return &memdbReader{nil, nil, datastore.NoRevision, fmt.Errorf("memdb datastore is already closed"), mdb.enableCaveats}
}

snapshotRevision := rev.revision
Expand All @@ -124,7 +135,7 @@ func (mdb *memdbDatastore) SnapshotReader(revision datastore.Revision) datastore
return roTxn, nil
}

return &memdbReader{noopTryLocker{}, txSrc, snapshotRevision, nil}
return &memdbReader{noopTryLocker{}, txSrc, snapshotRevision, nil, mdb.enableCaveats}
}

func (mdb *memdbDatastore) ReadWriteTx(
Expand Down Expand Up @@ -160,7 +171,7 @@ func (mdb *memdbDatastore) ReadWriteTx(

newRevision := revisionFromTimestamp(time.Now().UTC())

rwt := &memdbReadWriteTx{memdbReader{&sync.Mutex{}, txSrc, datastore.NoRevision, nil}, newRevision}
rwt := &memdbReadWriteTx{memdbReader{&sync.Mutex{}, txSrc, datastore.NoRevision, nil, mdb.enableCaveats}, newRevision}
if err := f(ctx, rwt); err != nil {
mdb.Lock()
if tx != nil {
Expand Down
7 changes: 4 additions & 3 deletions internal/datastore/memdb/readonly.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ type txFactory func() (*memdb.Txn, error)

type memdbReader struct {
TryLocker
txSource txFactory
revision datastore.Revision
initErr error
txSource txFactory
revision datastore.Revision
initErr error
enableCaveats bool
}

// QueryRelationships reads relationships starting from the resource side.
Expand Down
22 changes: 17 additions & 5 deletions internal/services/v1/relationships.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,25 @@ func (ps *permissionServer) WriteRelationships(ctx context.Context, req *v1.Writ
for _, update := range req.Updates {
// TODO(vroldanbet) we type assert CaveatReader for now because not all datastores implement it
// eventually this type assertion should be removed
if caveatReader, ok := rwt.(datastore.CaveatReader); update.Relationship.OptionalCaveat != nil && ok {
_, err := caveatReader.ReadCaveatByName(update.Relationship.OptionalCaveat.CaveatName)
if errors.As(err, &datastore.ErrCaveatNameNotFound{}) {
if update.Relationship.OptionalCaveat != nil && update.Relationship.OptionalCaveat.CaveatName != "" {
if caveatReader, ok := rwt.(datastore.CaveatReader); ok {
_, err := caveatReader.ReadCaveatByName(update.Relationship.OptionalCaveat.CaveatName)
if errors.As(err, &datastore.ErrCaveatNameNotFound{}) {
return status.Errorf(
codes.FailedPrecondition,
"the caveat `%s` was not found for relationship `%s`",
update.Relationship.OptionalCaveat.CaveatName,
tuple.StringRelationship(update.Relationship),
)
}

if err != nil {
return rewritePermissionsError(ctx, err)
}
} else {
return status.Errorf(
codes.FailedPrecondition,
"the caveat `%s` was not found for relationship `%s`",
update.Relationship.OptionalCaveat.CaveatName,
"caveats are currently not supported for this datastore; found for relationship `%s`",
tuple.StringRelationship(update.Relationship),
)
}
Expand Down
10 changes: 9 additions & 1 deletion pkg/cmd/datastore/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ type Config struct {

// Internal
WatchBufferLength uint16

// Experiments
ExperimentEnableCaveats bool
}

// RegisterDatastoreFlags adds datastore flags to a cobra command
Expand Down Expand Up @@ -118,6 +121,11 @@ func RegisterDatastoreFlags(cmd *cobra.Command, opts *Config) {
cmd.Flags().StringVar(&opts.SpannerEmulatorHost, "datastore-spanner-emulator-host", "", "URI of spanner emulator instance used for development and testing (e.g. localhost:9010)")
cmd.Flags().StringVar(&opts.TablePrefix, "datastore-mysql-table-prefix", "", "prefix to add to the name of all SpiceDB database tables")

cmd.Flags().BoolVar(&opts.ExperimentEnableCaveats, "experiment-enable-caveats", false, "if true, experimental support for caveats is enabled; note that these are not fully implemented and may break")
if err := cmd.Flags().MarkDeprecated("experiment-enable-caveats", "this is an experiment"); err != nil {
panic("failed to mark flag deprecated: " + err.Error())
}

// disabling stats is only for tests
cmd.Flags().BoolVar(&opts.DisableStats, "datastore-disable-stats", false, "disable recording relationship counts to the stats table")
if err := cmd.Flags().MarkHidden("datastore-disable-stats"); err != nil {
Expand Down Expand Up @@ -294,5 +302,5 @@ func newMySQLDatastore(opts Config) (datastore.Datastore, error) {

func newMemoryDatstore(opts Config) (datastore.Datastore, error) {
log.Warn().Msg("in-memory datastore is not persistent and not feasible to run in a high availability fashion")
return memdb.NewMemdbDatastore(opts.WatchBufferLength, opts.RevisionQuantization, opts.GCWindow)
return memdb.NewMemdbDatastoreWithCaveatsOption(opts.WatchBufferLength, opts.RevisionQuantization, opts.GCWindow, opts.ExperimentEnableCaveats)
}
8 changes: 8 additions & 0 deletions pkg/cmd/datastore/zz_generated.options.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ddce492

Please sign in to comment.