From a6bf04f2e40ab89f9b82a2da3ec14ff2eae3d156 Mon Sep 17 00:00:00 2001 From: Jussi Maki Date: Fri, 13 Mar 2026 16:37:51 +0100 Subject: [PATCH] Proposal: Split into multiple Go modules This is a proof-of-concept for showing how we could split the StateDB repo into multiple modules so we can have a core StateDB module with minimal dependencies. Only statedb/hive and statedb/reconciler depend on cilium/hive and cilium/stream. TODO: This commit needs to be split into multiple commits as it does too much. Signed-off-by: Jussi Maki --- any_table.go | 2 +- benchmarks_test.go | 29 ++----- db_test.go | 76 ++---------------- go.mod | 33 ++------ go.sum | 78 +----------------- go.work | 8 ++ cell.go => hive/cell.go | 14 ++-- derive.go => hive/derive.go | 9 ++- derive_test.go => hive/derive_test.go | 32 +++----- hive/go.mod | 48 +++++++++++ hive/go.sum | 90 +++++++++++++++++++++ hive/helpers_test.go | 94 ++++++++++++++++++++++ observable.go => hive/observable.go | 11 +-- hive/observable_test.go | 64 +++++++++++++++ script.go => hive/script.go | 90 ++++++++------------- script_test.go => hive/script_test.go | 13 +-- {testdata => hive/testdata}/db.txtar | 0 http.go | 4 +- iterator.go | 7 +- part/go.mod | 17 ++++ part/go.sum | 15 ++++ reconciler/benchmark/main.go | 3 +- reconciler/example/main.go | 3 +- reconciler/go.mod | 50 ++++++++++++ reconciler/go.sum | 90 +++++++++++++++++++++ reconciler/helpers.go | 26 ++++++ reconciler/multi_test.go | 3 +- reconciler/progress_test.go | 3 +- reconciler/script_test.go | 3 +- reconciler/types.go | 7 +- release.sh | 110 ++++++++++++++++++++++++++ table.go | 6 +- types.go | 12 ++- 33 files changed, 737 insertions(+), 313 deletions(-) create mode 100644 go.work rename cell.go => hive/cell.go (58%) rename derive.go => hive/derive.go (95%) rename derive_test.go => hive/derive_test.go (85%) create mode 100644 hive/go.mod create mode 100644 hive/go.sum create mode 100644 hive/helpers_test.go rename observable.go => hive/observable.go (79%) create mode 100644 hive/observable_test.go rename script.go => hive/script.go (87%) rename script_test.go => hive/script_test.go (91%) rename {testdata => hive/testdata}/db.txtar (100%) create mode 100644 part/go.mod create mode 100644 part/go.sum create mode 100644 reconciler/go.mod create mode 100644 reconciler/go.sum create mode 100755 release.sh diff --git a/any_table.go b/any_table.go index b73d3907..b29ca25c 100644 --- a/any_table.go +++ b/any_table.go @@ -110,7 +110,7 @@ func (t AnyTable) queryIndex(txn ReadTxn, index string, key string) (tableIndexR return itxn, rawKey, err } -func (t AnyTable) Changes(txn WriteTxn) (anyChangeIterator, error) { +func (t AnyTable) Changes(txn WriteTxn) (AnyChangeIterator, error) { return t.Meta.anyChanges(txn) } diff --git a/benchmarks_test.go b/benchmarks_test.go index 317a5665..90472380 100644 --- a/benchmarks_test.go +++ b/benchmarks_test.go @@ -4,10 +4,8 @@ package statedb import ( - "context" "fmt" "iter" - "log/slog" "math/rand" "slices" "testing" @@ -16,9 +14,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/cilium/hive" - "github.com/cilium/hive/cell" - "github.com/cilium/hive/hivetest" "github.com/cilium/statedb/index" "github.com/cilium/statedb/part" ) @@ -603,26 +598,12 @@ var ( func BenchmarkDB_PropagationDelay(b *testing.B) { const batchSize = 10 - var ( - db *DB - table1 RWTable[*testObject] - table2 RWTable[*testObject2] - ) - - h := hive.New( - Cell, // DB - cell.Invoke(func(db_ *DB) error { - db = db_ - table1 = MustNewTable(db, "test", idIndex) - table2 = MustNewTable(db, "test2", id2Index) - return nil - }), - ) - - log := hivetest.Logger(b, hivetest.LogLevel(slog.LevelError)) - require.NoError(b, h.Start(log, context.TODO())) + db := New() + table1 := MustNewTable(db, "test", idIndex) + table2 := MustNewTable(db, "test2", id2Index) + require.NoError(b, db.Start()) b.Cleanup(func() { - assert.NoError(b, h.Stop(log, context.TODO())) + assert.NoError(b, db.Stop()) }) b.ResetTimer() diff --git a/db_test.go b/db_test.go index 1c9b205a..0ef4401e 100644 --- a/db_test.go +++ b/db_test.go @@ -5,13 +5,11 @@ package statedb import ( "bytes" - "context" "encoding/json" "errors" "expvar" "fmt" "iter" - "log/slog" "net/netip" "runtime" "slices" @@ -24,12 +22,8 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/goleak" - "github.com/cilium/hive" - "github.com/cilium/hive/cell" - "github.com/cilium/hive/hivetest" "github.com/cilium/statedb/index" "github.com/cilium/statedb/part" - "github.com/cilium/stream" ) // Amount of time to wait for the watch channel to close in tests @@ -149,28 +143,12 @@ func newTestDB(t testing.TB, secondaryIndexers ...Indexer[*testObject]) (*DB, RW } func newTestDBWithMetrics(t testing.TB, metrics Metrics, secondaryIndexers ...Indexer[*testObject]) (*DB, RWTable[*testObject]) { - var ( - db *DB - table RWTable[*testObject] - ) - - h := hive.New( - cell.Provide(func() Metrics { return metrics }), - Cell, // DB - cell.Invoke(func(db_ *DB) { - table = newTestObjectTable(t, db_, "test", secondaryIndexers...) - - // Use a short GC interval. - db_.setGCRateLimitInterval(50 * time.Millisecond) - - db = db_ - }), - ) - - log := hivetest.Logger(t, hivetest.LogLevel(slog.LevelError)) - require.NoError(t, h.Start(log, context.TODO())) + db := New(WithMetrics(metrics)) + table := newTestObjectTable(t, db, "test", secondaryIndexers...) + db.setGCRateLimitInterval(50 * time.Millisecond) + require.NoError(t, db.Start()) t.Cleanup(func() { - assert.NoError(t, h.Stop(log, context.TODO())) + assert.NoError(t, db.Stop()) }) return db, table } @@ -617,50 +595,6 @@ func TestDB_Changes(t *testing.T) { wtxn.Abort() } -func TestDB_Observable(t *testing.T) { - t.Parallel() - - db, table, _ := newTestDB(t) - ctx, cancel := context.WithCancel(context.Background()) - events := stream.ToChannel(ctx, Observable(db, table)) - - txn := db.WriteTxn(table) - _, hadOld, err := table.Insert(txn, &testObject{ID: uint64(1)}) - require.False(t, hadOld, "Expected no prior object") - require.NoError(t, err, "Insert failed") - _, hadOld, err = table.Insert(txn, &testObject{ID: uint64(2)}) - require.False(t, hadOld, "Expected no prior object") - require.NoError(t, err, "Insert failed") - txn.Commit() - - event := <-events - require.False(t, event.Deleted, "expected insert") - require.Equal(t, uint64(1), event.Object.ID) - event = <-events - require.False(t, event.Deleted, "expected insert") - require.Equal(t, uint64(2), event.Object.ID) - - txn = db.WriteTxn(table) - _, hadOld, err = table.Delete(txn, &testObject{ID: uint64(1)}) - require.True(t, hadOld, "Expected that object was deleted") - require.NoError(t, err, "Delete failed") - _, hadOld, err = table.Delete(txn, &testObject{ID: uint64(2)}) - require.True(t, hadOld, "Expected that object was deleted") - require.NoError(t, err, "Delete failed") - txn.Commit() - - event = <-events - require.True(t, event.Deleted, "expected delete") - require.Equal(t, uint64(1), event.Object.ID) - event = <-events - require.True(t, event.Deleted, "expected delete") - require.Equal(t, uint64(2), event.Object.ID) - - cancel() - ev, ok := <-events - require.False(t, ok, "expected channel to close, got event: %+v", ev) -} - func TestDB_NumObjects(t *testing.T) { t.Parallel() diff --git a/go.mod b/go.mod index 62cc6b40..ff12fbd5 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,7 @@ module github.com/cilium/statedb go 1.25 require ( - github.com/cilium/hive v1.0.0 - github.com/cilium/stream v0.0.0-20240209152734-a0792b51812d - github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de - github.com/spf13/cobra v1.8.0 - github.com/spf13/pflag v1.0.5 + github.com/cilium/statedb/part v0.0.0 github.com/stretchr/testify v1.8.4 go.uber.org/goleak v1.3.0 go.yaml.in/yaml/v3 v3.0.3 @@ -16,28 +12,11 @@ require ( require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.6.0 // indirect - github.com/spf13/viper v1.18.2 // indirect - github.com/subosito/gotenv v1.6.0 // indirect - go.uber.org/dig v1.17.1 // indirect - go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect - golang.org/x/sys v0.17.0 // indirect - golang.org/x/term v0.16.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.17.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/ini.v1 v1.67.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/cilium/statedb/part => ./part diff --git a/go.sum b/go.sum index 7285f207..94493754 100644 --- a/go.sum +++ b/go.sum @@ -1,93 +1,23 @@ -github.com/cilium/hive v1.0.0 h1:HEPvIFBCgAWOBclSVHoDXGpRB5SPgzMXdB54BOrITKE= -github.com/cilium/hive v1.0.0/go.mod h1:4/8FBMcTjVdkrNNWaB7t3QqaU4kZDJLJ1leKVP9GjEI= -github.com/cilium/stream v0.0.0-20240209152734-a0792b51812d h1:p6MgATaKEB9o7iAsk9rlzXNDMNCeKPAkx4Y8f+Zq8X8= -github.com/cilium/stream v0.0.0-20240209152734-a0792b51812d/go.mod h1:3VLiLgs8wfjirkuYqos4t0IBPQ+sXtf3tFkChLm6ARM= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= -github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= -go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= -golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= -golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go.work b/go.work new file mode 100644 index 00000000..0ce9623b --- /dev/null +++ b/go.work @@ -0,0 +1,8 @@ +go 1.25 + +use ( + . + ./hive + ./part + ./reconciler +) diff --git a/cell.go b/hive/cell.go similarity index 58% rename from cell.go rename to hive/cell.go index 9a233950..2c1af63a 100644 --- a/cell.go +++ b/hive/cell.go @@ -1,15 +1,15 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Authors of Cilium -package statedb +package hive import ( "github.com/cilium/hive/cell" + "github.com/cilium/statedb" ) -// This module provides an in-memory database built on top of immutable radix trees -// As the database is based on an immutable data structure, the objects inserted into -// the database MUST NOT be mutated, but rather copied first! +// Cell is a [cell.Module] that provides a [*statedb.DB] instance and +// registers the statedb script commands for use in hive script tests. var Cell = cell.Module( "statedb", "In-memory transactional database", @@ -24,11 +24,11 @@ type params struct { cell.In Lifecycle cell.Lifecycle - Metrics Metrics `optional:"true"` + Metrics statedb.Metrics `optional:"true"` } -func newHiveDB(p params) *DB { - db := New(WithMetrics(p.Metrics)) +func newHiveDB(p params) *statedb.DB { + db := statedb.New(statedb.WithMetrics(p.Metrics)) p.Lifecycle.Append( cell.Hook{ OnStart: func(cell.HookContext) error { diff --git a/derive.go b/hive/derive.go similarity index 95% rename from derive.go rename to hive/derive.go index 49fec9f1..0e20a8f0 100644 --- a/derive.go +++ b/hive/derive.go @@ -1,13 +1,14 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Authors of Cilium -package statedb +package hive import ( "context" "github.com/cilium/hive/cell" "github.com/cilium/hive/job" + "github.com/cilium/statedb" ) type DeriveResult int @@ -24,9 +25,9 @@ type DeriveParams[In, Out any] struct { Lifecycle cell.Lifecycle JobGroup job.Group - DB *DB - InTable Table[In] - OutTable RWTable[Out] + DB *statedb.DB + InTable statedb.Table[In] + OutTable statedb.RWTable[Out] } // Derive constructs and registers a job to transform objects from the input table to the diff --git a/derive_test.go b/hive/derive_test.go similarity index 85% rename from derive_test.go rename to hive/derive_test.go index 288b2425..285e09b9 100644 --- a/derive_test.go +++ b/hive/derive_test.go @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Authors of Cilium -package statedb +package hive import ( "context" @@ -13,10 +13,11 @@ import ( "github.com/stretchr/testify/require" - "github.com/cilium/hive" + ciliumhive "github.com/cilium/hive" "github.com/cilium/hive/cell" "github.com/cilium/hive/hivetest" "github.com/cilium/hive/job" + "github.com/cilium/statedb" "github.com/cilium/statedb/index" "github.com/cilium/statedb/part" ) @@ -26,12 +27,10 @@ type derived struct { Deleted bool } -// TableHeader implements TableWritable. func (d derived) TableHeader() []string { return []string{"ID", "Deleted"} } -// TableRow implements TableWritable. func (d derived) TableRow() []string { return []string{ fmt.Sprintf("%d", d.ID), @@ -39,9 +38,9 @@ func (d derived) TableRow() []string { } } -var _ TableWritable = derived{} +var _ statedb.TableWritable = derived{} -var derivedIdIndex = Index[derived, uint64]{ +var derivedIdIndex = statedb.Index[derived, uint64]{ Name: "id", FromObject: func(t derived) index.KeySet { return index.NewKeySet(index.Uint64(t.ID)) @@ -53,20 +52,16 @@ var derivedIdIndex = Index[derived, uint64]{ type nopHealth struct { } -// Degraded implements cell.Health. func (*nopHealth) Degraded(reason string, err error) { } -// NewScope implements cell.Health. func (h *nopHealth) NewScope(name string) cell.Health { return h } -// OK implements cell.Health. func (*nopHealth) OK(status string) { } -// Stopped implements cell.Health. func (*nopHealth) Stopped(reason string) { } @@ -83,9 +78,9 @@ func TestDerive(t *testing.T) { t.Parallel() var ( - db *DB - inTable RWTable[*testObject] - outTable RWTable[derived] + db *statedb.DB + inTable statedb.RWTable[*testObject] + outTable statedb.RWTable[derived] ) transform := func(obj *testObject, deleted bool) (derived, DeriveResult) { @@ -104,7 +99,7 @@ func TestDerive(t *testing.T) { return derived{ID: obj.ID, Deleted: false}, DeriveInsert } - h := hive.New( + h := ciliumhive.New( Cell, // DB job.Cell, cell.Provide(newNopHealth), @@ -112,10 +107,10 @@ func TestDerive(t *testing.T) { "test", "Test", cell.Provide( - func(db_ *DB) (Table[*testObject], RWTable[derived], error) { + func(db_ *statedb.DB) (statedb.Table[*testObject], statedb.RWTable[derived], error) { db = db_ - inTable = MustNewTable(db, "test", idIndex) - outTable = MustNewTable(db, "derived", derivedIdIndex) + inTable = statedb.MustNewTable(db, "test", idIndex) + outTable = statedb.MustNewTable(db, "derived", derivedIdIndex) return inTable, outTable, nil }, job.Registry.NewGroup, @@ -129,8 +124,7 @@ func TestDerive(t *testing.T) { getDerived := func() []derived { txn := db.ReadTxn() - objs := Collect(outTable.All(txn)) - // Log so we can trace the failed eventually calls + objs := statedb.Collect(outTable.All(txn)) t.Logf("derived: %+v", objs) return objs } diff --git a/hive/go.mod b/hive/go.mod new file mode 100644 index 00000000..4da11b3e --- /dev/null +++ b/hive/go.mod @@ -0,0 +1,48 @@ +module github.com/cilium/statedb/hive + +go 1.25 + +require ( + github.com/cilium/hive v1.0.0 + github.com/cilium/statedb v0.0.0 + github.com/cilium/statedb/part v0.0.0 + github.com/cilium/stream v0.0.0-20240209152734-a0792b51812d + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de + github.com/spf13/pflag v1.0.5 + github.com/stretchr/testify v1.8.4 + go.yaml.in/yaml/v3 v3.0.3 + golang.org/x/time v0.5.0 +) + +require ( + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/viper v1.18.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/dig v1.17.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/term v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.16.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace ( + github.com/cilium/statedb => ../ + github.com/cilium/statedb/part => ../part +) diff --git a/hive/go.sum b/hive/go.sum new file mode 100644 index 00000000..1ee06554 --- /dev/null +++ b/hive/go.sum @@ -0,0 +1,90 @@ +github.com/cilium/hive v1.0.0 h1:HEPvIFBCgAWOBclSVHoDXGpRB5SPgzMXdB54BOrITKE= +github.com/cilium/hive v1.0.0/go.mod h1:4/8FBMcTjVdkrNNWaB7t3QqaU4kZDJLJ1leKVP9GjEI= +github.com/cilium/stream v0.0.0-20240209152734-a0792b51812d h1:p6MgATaKEB9o7iAsk9rlzXNDMNCeKPAkx4Y8f+Zq8X8= +github.com/cilium/stream v0.0.0-20240209152734-a0792b51812d/go.mod h1:3VLiLgs8wfjirkuYqos4t0IBPQ+sXtf3tFkChLm6ARM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= +go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= +go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= +golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8= +golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/hive/helpers_test.go b/hive/helpers_test.go new file mode 100644 index 00000000..7d1b83b4 --- /dev/null +++ b/hive/helpers_test.go @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Cilium + +package hive + +import ( + "encoding/json" + "fmt" + "iter" + "net/netip" + "slices" + "strconv" + "strings" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cilium/statedb" + "github.com/cilium/statedb/index" + "github.com/cilium/statedb/part" +) + +type testObject struct { + ID uint64 `json:"id" yaml:"id"` + Key string `json:"key,omitempty" yaml:"key,omitempty"` + Prefix netip.Prefix `yaml:"prefix"` + Tags part.Set[string] `json:"tags" yaml:"tags"` +} + +func (t *testObject) TableHeader() []string { + return []string{"ID", "Key", "Prefix", "Tags"} +} + +func (t *testObject) TableRow() []string { + return []string{ + strconv.FormatUint(uint64(t.ID), 10), + t.Key, + t.Prefix.String(), + strings.Join(slices.Collect(t.Tags.All()), ", "), + } +} + +func (t *testObject) MarshalJSON() ([]byte, error) { + t2 := struct { + ID uint64 + Tags part.Set[string] + }{t.ID, t.Tags} + return json.Marshal(t2) +} + +func (t *testObject) String() string { + return fmt.Sprintf("testObject{ID: %d, Tags: %v}", t.ID, t.Tags) +} + +var ( + idIndex = statedb.Index[*testObject, uint64]{ + Name: "id", + FromObject: func(t *testObject) index.KeySet { + return index.NewKeySet(index.Uint64(t.ID)) + }, + FromKey: index.Uint64, + FromString: index.Uint64String, + Unique: true, + } + + tagsIndex = statedb.Index[*testObject, string]{ + Name: "tags", + FromObject: func(t *testObject) index.KeySet { + return index.Set(t.Tags) + }, + FromKey: index.String, + FromString: index.FromString, + Unique: false, + } + + prefixIndex = statedb.NetIPPrefixIndex[*testObject]{ + Name: "prefix", + FromObject: func(obj *testObject) iter.Seq[netip.Prefix] { + return statedb.Just(obj.Prefix) + }, + Unique: true, + } +) + +func newTestTable(t testing.TB, db *statedb.DB, name string, secondaryIndexers ...statedb.Indexer[*testObject]) statedb.RWTable[*testObject] { + table, err := statedb.NewTable( + db, + name, + idIndex, + secondaryIndexers..., + ) + require.NoError(t, err, "NewTable[testObject]") + return table +} diff --git a/observable.go b/hive/observable.go similarity index 79% rename from observable.go rename to hive/observable.go index c856d577..0f8b3c97 100644 --- a/observable.go +++ b/hive/observable.go @@ -1,11 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Authors of Cilium -package statedb +package hive import ( "context" + "github.com/cilium/statedb" "github.com/cilium/stream" ) @@ -14,16 +15,16 @@ import ( // // For high-churn tables it's advisable to apply rate-limiting to the stream to // decrease overhead (stream.Throttle). -func Observable[Obj any](db *DB, table Table[Obj]) stream.Observable[Change[Obj]] { +func Observable[Obj any](db *statedb.DB, table statedb.Table[Obj]) stream.Observable[statedb.Change[Obj]] { return &observable[Obj]{db, table} } type observable[Obj any] struct { - db *DB - table Table[Obj] + db *statedb.DB + table statedb.Table[Obj] } -func (to *observable[Obj]) Observe(ctx context.Context, next func(Change[Obj]), complete func(error)) { +func (to *observable[Obj]) Observe(ctx context.Context, next func(statedb.Change[Obj]), complete func(error)) { go func() { txn := to.db.WriteTxn(to.table) iter, err := to.table.Changes(txn) diff --git a/hive/observable_test.go b/hive/observable_test.go new file mode 100644 index 00000000..42e6e6ce --- /dev/null +++ b/hive/observable_test.go @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Cilium + +package hive + +import ( + "context" + "testing" + + "github.com/cilium/statedb" + "github.com/cilium/stream" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestObservable(t *testing.T) { + t.Parallel() + + db := statedb.New() + table := newTestTable(t, db, "test") + require.NoError(t, db.Start()) + t.Cleanup(func() { + assert.NoError(t, db.Stop()) + }) + + ctx, cancel := context.WithCancel(context.Background()) + events := stream.ToChannel(ctx, Observable(db, table)) + + txn := db.WriteTxn(table) + _, hadOld, err := table.Insert(txn, &testObject{ID: uint64(1)}) + require.False(t, hadOld, "Expected no prior object") + require.NoError(t, err, "Insert failed") + _, hadOld, err = table.Insert(txn, &testObject{ID: uint64(2)}) + require.False(t, hadOld, "Expected no prior object") + require.NoError(t, err, "Insert failed") + txn.Commit() + + event := <-events + require.False(t, event.Deleted, "expected insert") + require.Equal(t, uint64(1), event.Object.ID) + event = <-events + require.False(t, event.Deleted, "expected insert") + require.Equal(t, uint64(2), event.Object.ID) + + txn = db.WriteTxn(table) + _, hadOld, err = table.Delete(txn, &testObject{ID: uint64(1)}) + require.True(t, hadOld, "Expected that object was deleted") + require.NoError(t, err, "Delete failed") + _, hadOld, err = table.Delete(txn, &testObject{ID: uint64(2)}) + require.True(t, hadOld, "Expected that object was deleted") + require.NoError(t, err, "Delete failed") + txn.Commit() + + event = <-events + require.True(t, event.Deleted, "expected delete") + require.Equal(t, uint64(1), event.Object.ID) + event = <-events + require.True(t, event.Deleted, "expected delete") + require.Equal(t, uint64(2), event.Object.ID) + + cancel() + ev, ok := <-events + require.False(t, ok, "expected channel to close, got event: %+v", ev) +} diff --git a/script.go b/hive/script.go similarity index 87% rename from script.go rename to hive/script.go index 414fa32e..6e1c3cad 100644 --- a/script.go +++ b/hive/script.go @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Authors of Cilium -package statedb +package hive import ( "bytes" @@ -18,13 +18,14 @@ import ( "github.com/cilium/hive" "github.com/cilium/hive/script" + "github.com/cilium/statedb" "github.com/liggitt/tabwriter" "github.com/spf13/pflag" "go.yaml.in/yaml/v3" "golang.org/x/time/rate" ) -func ScriptCommands(db *DB) hive.ScriptCmdsOut { +func ScriptCommands(db *statedb.DB) hive.ScriptCmdsOut { return hive.NewScriptCmds(map[string]script.Cmd{ "db": DBCmd(db), "db/show": ShowCmd(db), @@ -41,7 +42,7 @@ func ScriptCommands(db *DB) hive.ScriptCmdsOut { }) } -func DBCmd(db *DB) script.Cmd { +func DBCmd(db *statedb.DB) script.Cmd { return script.Command( script.CmdUsage{ Summary: "Describe StateDB configuration", @@ -79,13 +80,13 @@ func DBCmd(db *DB) script.Cmd { func(s *script.State, args ...string) (script.WaitFunc, error) { txn := db.ReadTxn() tbls := db.GetTables(txn) - slices.SortFunc(tbls, func(a, b TableMeta) int { return cmp.Compare(a.Name(), b.Name()) }) + slices.SortFunc(tbls, func(a, b statedb.TableMeta) int { return cmp.Compare(a.Name(), b.Name()) }) w := newTabWriter(s.LogWriter()) fmt.Fprintf(w, "Name\tObject count\tZombie objects\tIndexes\tInitializers\tGo type\tLast WriteTxn\n") for _, tbl := range tbls { idxs := strings.Join(tbl.Indexes(), ", ") fmt.Fprintf(w, "%s\t%d\t%d\t%s\t%v\t%s\t%s\n", - tbl.Name(), tbl.NumObjects(txn), tbl.numDeletedObjects(txn), idxs, tbl.PendingInitializers(txn), tbl.typeName(), tbl.getAcquiredInfo()) + tbl.Name(), tbl.NumObjects(txn), tbl.NumDeletedObjects(txn), idxs, tbl.PendingInitializers(txn), tbl.TypeName(), tbl.AcquiredInfo()) } w.Flush() return nil, nil @@ -93,7 +94,7 @@ func DBCmd(db *DB) script.Cmd { ) } -func InitializedCmd(db *DB) script.Cmd { +func InitializedCmd(db *statedb.DB) script.Cmd { return script.Command( script.CmdUsage{ Summary: "Wait until all or specific tables have been initialized", @@ -126,7 +127,7 @@ func InitializedCmd(db *DB) script.Cmd { if len(args) > 0 { // Specific tables requested, look them up. - tbls = make([]TableMeta, 0, len(args)) + tbls = make([]statedb.TableMeta, 0, len(args)) for _, tableName := range args { found := false for _, tbl := range allTbls { @@ -163,7 +164,7 @@ func InitializedCmd(db *DB) script.Cmd { ) } -func autocompleteTableName(db *DB, before []string, cur string) []string { +func autocompleteTableName(db *statedb.DB, before []string, cur string) []string { var suggestions []string for _, tbl := range db.GetTables(db.ReadTxn()) { if cur == "" || strings.HasPrefix(tbl.Name(), cur) { @@ -174,7 +175,7 @@ func autocompleteTableName(db *DB, before []string, cur string) []string { return suggestions } -func autocompleteColumnNames(db *DB, args []string, cur string) []string { +func autocompleteColumnNames(db *statedb.DB, args []string, cur string) []string { if len(args) < 1 { return nil } @@ -206,7 +207,7 @@ func autocompleteColumnNames(db *DB, args []string, cur string) []string { return suggestions } -func ShowCmd(db *DB) script.Cmd { +func ShowCmd(db *statedb.DB) script.Cmd { return script.Command( script.CmdUsage{ Summary: "Show the contents of a table", @@ -286,7 +287,7 @@ func ShowCmd(db *DB) script.Cmd { }) } -func CompareCmd(db *DB) script.Cmd { +func CompareCmd(db *statedb.DB) script.Cmd { return script.Command( script.CmdUsage{ Summary: "Compare table", @@ -347,7 +348,7 @@ func CompareCmd(db *DB) script.Cmd { if meta == nil { return nil, fmt.Errorf("table %q not found", tableName) } - tbl := AnyTable{Meta: meta} + tbl := statedb.AnyTable{Meta: meta} header := tbl.TableHeader() data, err := os.ReadFile(s.Path(fileName)) @@ -387,7 +388,7 @@ func CompareCmd(db *DB) script.Cmd { objs, watch := tbl.AllWatch(db.ReadTxn()) for obj := range objs { - rowRaw := takeColumns(obj.(TableWritable).TableRow(), columnIndexes) + rowRaw := takeColumns(obj.(statedb.TableWritable).TableRow(), columnIndexes) row := joinByPositions(rowRaw, columnPositions, padByTabs) if grepRe != nil && !grepRe.Match([]byte(row)) { continue @@ -438,7 +439,7 @@ func CompareCmd(db *DB) script.Cmd { }) } -func EmptyCmd(db *DB) script.Cmd { +func EmptyCmd(db *statedb.DB) script.Cmd { return script.Command( script.CmdUsage{ Summary: "Assert that given table(s) are empty", @@ -459,7 +460,7 @@ func EmptyCmd(db *DB) script.Cmd { if meta == nil { return nil, fmt.Errorf("table %q not found", tableName) } - tbl := AnyTable{Meta: meta} + tbl := statedb.AnyTable{Meta: meta} if n := tbl.NumObjects(txn); n != 0 { return nil, fmt.Errorf("table %q not empty, found %d obects", tableName, n) } @@ -468,7 +469,7 @@ func EmptyCmd(db *DB) script.Cmd { }) } -func InsertCmd(db *DB) script.Cmd { +func InsertCmd(db *statedb.DB) script.Cmd { return script.Command( script.CmdUsage{ Summary: "Insert object into a table", @@ -492,7 +493,7 @@ func InsertCmd(db *DB) script.Cmd { ) } -func DeleteCmd(db *DB) script.Cmd { +func DeleteCmd(db *statedb.DB) script.Cmd { return script.Command( script.CmdUsage{ Summary: "Delete an object from the table", @@ -517,16 +518,16 @@ func DeleteCmd(db *DB) script.Cmd { ) } -func getTable(db *DB, tableName string) (*AnyTable, ReadTxn, error) { +func getTable(db *statedb.DB, tableName string) (*statedb.AnyTable, statedb.ReadTxn, error) { txn := db.ReadTxn() meta := db.GetTable(txn, tableName) if meta == nil { return nil, nil, fmt.Errorf("table %q not found", tableName) } - return &AnyTable{Meta: meta}, txn, nil + return &statedb.AnyTable{Meta: meta}, txn, nil } -func insertOrDelete(insert bool, db *DB, s *script.State, args ...string) (script.WaitFunc, error) { +func insertOrDelete(insert bool, db *statedb.DB, s *script.State, args ...string) (script.WaitFunc, error) { if len(args) < 2 { return nil, fmt.Errorf("expected table and path(s)") } @@ -567,7 +568,7 @@ func insertOrDelete(insert bool, db *DB, s *script.State, args ...string) (scrip return nil, nil } -func PrefixCmd(db *DB) script.Cmd { +func PrefixCmd(db *statedb.DB) script.Cmd { return queryCmd(db, queryCmdPrefix, "Query table by prefix", @@ -577,7 +578,7 @@ func PrefixCmd(db *DB) script.Cmd { ) } -func LowerBoundCmd(db *DB) script.Cmd { +func LowerBoundCmd(db *statedb.DB) script.Cmd { return queryCmd(db, queryCmdLowerBound, "Query table by lower bound search", @@ -588,7 +589,7 @@ func LowerBoundCmd(db *DB) script.Cmd { ) } -func ListCmd(db *DB) script.Cmd { +func ListCmd(db *statedb.DB) script.Cmd { return queryCmd(db, queryCmdList, "List objects in the table", @@ -598,7 +599,7 @@ func ListCmd(db *DB) script.Cmd { ) } -func GetCmd(db *DB) script.Cmd { +func GetCmd(db *statedb.DB) script.Cmd { return queryCmd(db, queryCmdGet, "Get the first matching object", @@ -615,7 +616,7 @@ const ( queryCmdGet ) -func queryCmd(db *DB, query int, summary string, detail []string) script.Cmd { +func queryCmd(db *statedb.DB, query int, summary string, detail []string) script.Cmd { return script.Command( script.CmdUsage{ Summary: summary, @@ -652,7 +653,7 @@ func queryCmd(db *DB, query int, summary string, detail []string) script.Cmd { ) } -func runQueryCmd(query int, db *DB, s *script.State, args []string) (script.WaitFunc, error) { +func runQueryCmd(query int, db *statedb.DB, s *script.State, args []string) (script.WaitFunc, error) { file, err := s.Flags.GetString("out") if err != nil { return nil, err @@ -743,7 +744,7 @@ func runQueryCmd(query int, db *DB, s *script.State, args []string) (script.Wait }, nil } -func WatchCmd(db *DB) script.Cmd { +func WatchCmd(db *statedb.DB) script.Cmd { return script.Command( script.CmdUsage{ Summary: "Watch a table for changes", @@ -789,9 +790,9 @@ func WatchCmd(db *DB) script.Cmd { if err := limiter.Wait(s.Context()); err != nil { break } - changes, watch := iter.nextAny(db.ReadTxn()) + changes, watch := iter.NextAny(db.ReadTxn()) for change := range changes { - row := change.Object.(TableWritable).TableRow() + row := change.Object.(statedb.TableWritable).TableRow() if change.Deleted { fmt.Fprintf(tw, "%s (deleted)%s", strings.Join(row, "\t"), magicStrikethroughNewline) } else { @@ -823,7 +824,7 @@ func firstOfSeq2[A, B any](it iter.Seq2[A, B]) iter.Seq2[A, B] { } } -func writeObjects(tbl *AnyTable, it iter.Seq2[any, Revision], w io.Writer, columns []string, format string) error { +func writeObjects(tbl *statedb.AnyTable, it iter.Seq2[any, statedb.Revision], w io.Writer, columns []string, format string) error { if len(columns) > 0 && format != "table" { return fmt.Errorf("--columns not supported with non-table formats") } @@ -910,11 +911,6 @@ loop: return columnIndexes, nil } -// splitHeaderLine takes a header of column names separated by any -// number of whitespaces and returns the names and their starting positions. -// e.g. "Foo Bar Baz" would result in ([Foo,Bar,Baz],[0,5,9]). -// With this information we can take a row in the database and format it -// the same way as our test data. func splitHeaderLine(line string) (names []string, pos []int) { start := 0 skip := true @@ -941,13 +937,6 @@ func splitHeaderLine(line string) (names []string, pos []int) { return } -// splitByPositions takes a "row" line and the positions of the header columns -// and extracts the values. -// e.g. if we have the positions [0,5,9] (from header "Foo Bar Baz") and -// line is "1 a b", then we'd extract [1,a,b]. -// The whitespace on the right of the start position (e.g. "1 \t") is trimmed. -// This of course requires that the table is properly formatted in a way that the -// header columns are indented to fit the data exactly. func splitByPositions(line string, positions []int, splitByTabs bool) []string { out := make([]string, 0, len(positions)) start := 0 @@ -974,11 +963,6 @@ func splitByPositions(line string, positions []int, splitByTabs bool) []string { return out } -// joinByPositions is the reverse of splitByPositions, it takes the columns of a -// row and the starting positions of each and joins into a single line. -// e.g. [1,a,b] and positions [0,5,9] expands to "1 a b". -// NOTE: This does not deal well with mixing tabs and spaces. The test input -// data should preferably just use spaces. func joinByPositions(row []string, positions []int, padByTabs bool) string { var w strings.Builder prev := 0 @@ -997,10 +981,6 @@ func joinByPositions(row []string, positions []int, padByTabs bool) string { return w.String() } -// strikethroughWriter writes a line of text that is striken through -// if the line contains the magic character at the end before \n. -// This is used to strike through a tab-formatted line without messing -// up with the widths of the cells. type strikethroughWriter struct { buf []byte strikethrough bool @@ -1008,10 +988,6 @@ type strikethroughWriter struct { } var ( - // Magic character to use at the end of the line to denote that this should be - // striken through. - // This is to avoid messing up the width calculations in the tab writer, which - // would happen if ANSI codes were used directly. magicStrikethrough = byte('\xfe') magicStrikethroughNewline = "\xfe\n" ) @@ -1070,7 +1046,6 @@ func (s *strikethroughWriter) Write(p []byte) (n int, err error) { } var ( - // Use color red and the strikethrough escape beginStrikethrough = []byte("\033[9m\033[31m") endStrikethrough = []byte("\033[0m") newline = []byte("\n") @@ -1089,9 +1064,6 @@ func newTabWriter(out io.Writer) *tabwriter.Writer { return tabwriter.NewWriter(out, minWidth, width, padding, padChar, flags) } -// sortArgs sorts the arguments to bring '-arg' first. Allows mixing -// the argument order. If e.g. key starts with '-', then it'll just -// need to be quoted: "db/get foo '-mykey'" func sortedArgs(args []string) []string { return slices.SortedStableFunc( slices.Values(args), diff --git a/script_test.go b/hive/script_test.go similarity index 91% rename from script_test.go rename to hive/script_test.go index 35be2024..0580a290 100644 --- a/script_test.go +++ b/hive/script_test.go @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Authors of Cilium -package statedb +package hive import ( "context" @@ -10,11 +10,12 @@ import ( "strings" "testing" - "github.com/cilium/hive" + ciliumhive "github.com/cilium/hive" "github.com/cilium/hive/cell" "github.com/cilium/hive/hivetest" "github.com/cilium/hive/script" "github.com/cilium/hive/script/scripttest" + "github.com/cilium/statedb" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -22,11 +23,11 @@ import ( func TestScript(t *testing.T) { t.Parallel() log := hivetest.Logger(t) - h := hive.New( + h := ciliumhive.New( Cell, // DB - cell.Invoke(func(db *DB) { - _ = newTestObjectTable(t, db, "test1", tagsIndex, prefixIndex) - _ = newTestObjectTable(t, db, "test2", tagsIndex, prefixIndex) + cell.Invoke(func(db *statedb.DB) { + _ = newTestTable(t, db, "test1", tagsIndex, prefixIndex) + _ = newTestTable(t, db, "test2", tagsIndex, prefixIndex) }), ) t.Cleanup(func() { diff --git a/testdata/db.txtar b/hive/testdata/db.txtar similarity index 100% rename from testdata/db.txtar rename to hive/testdata/db.txtar diff --git a/http.go b/http.go index f6ed9ce6..0410367c 100644 --- a/http.go +++ b/http.go @@ -158,7 +158,7 @@ func (h dbHandler) changes(w http.ResponseWriter, r *http.Request) { // Register for changes. wtxn := h.db.WriteTxn(tableMeta) - changeIter, err := tableMeta.anyChanges(wtxn) + changeIter, err := AnyTable{Meta: tableMeta}.Changes(wtxn) wtxn.Commit() if err != nil { w.WriteHeader(http.StatusInternalServerError) @@ -171,7 +171,7 @@ func (h dbHandler) changes(w http.ResponseWriter, r *http.Request) { defer ticker.Stop() for { - changes, watch := changeIter.nextAny(h.db.ReadTxn()) + changes, watch := changeIter.NextAny(h.db.ReadTxn()) for change := range changes { err := enc.Encode(change) if err != nil { diff --git a/iterator.go b/iterator.go index f83d4619..95beee59 100644 --- a/iterator.go +++ b/iterator.go @@ -234,7 +234,7 @@ func (it *changeIterator[Obj]) Next(txn ReadTxn) (seq iter.Seq2[Change[Obj], Rev // changesAny is for implementing the /changes HTTP API where the concrete object // type is not known. -func (it *changeIterator[Obj]) nextAny(txn ReadTxn) (iter.Seq2[Change[any], Revision], <-chan struct{}) { +func (it *changeIterator[Obj]) NextAny(txn ReadTxn) (iter.Seq2[Change[any], Revision], <-chan struct{}) { seq, watch := it.Next(txn) return func(yield func(Change[any], Revision) bool) { @@ -259,6 +259,7 @@ func (it *changeIterator[Obj]) Close() { it.dt = nil } -type anyChangeIterator interface { - nextAny(ReadTxn) (iter.Seq2[Change[any], Revision], <-chan struct{}) +// AnyChangeIterator is an untyped change iterator returned by [AnyTable.Changes]. +type AnyChangeIterator interface { + NextAny(ReadTxn) (iter.Seq2[Change[any], Revision], <-chan struct{}) } diff --git a/part/go.mod b/part/go.mod new file mode 100644 index 00000000..5a832c07 --- /dev/null +++ b/part/go.mod @@ -0,0 +1,17 @@ +module github.com/cilium/statedb/part + +go 1.25 + +require ( + github.com/stretchr/testify v1.8.4 + go.yaml.in/yaml/v3 v3.0.3 +) + +require ( + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/part/go.sum b/part/go.sum new file mode 100644 index 00000000..09ae44f8 --- /dev/null +++ b/part/go.sum @@ -0,0 +1,15 @@ +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= +go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/reconciler/benchmark/main.go b/reconciler/benchmark/main.go index 3cb5247c..49a40123 100644 --- a/reconciler/benchmark/main.go +++ b/reconciler/benchmark/main.go @@ -20,6 +20,7 @@ import ( "github.com/cilium/hive/cell" "github.com/cilium/hive/job" "github.com/cilium/statedb" + statedbhive "github.com/cilium/statedb/hive" "github.com/cilium/statedb/index" "github.com/cilium/statedb/reconciler" "golang.org/x/time/rate" @@ -126,7 +127,7 @@ func main() { hive := hive.New( cell.SimpleHealthCell, - statedb.Cell, + statedbhive.Cell, job.Cell, cell.Module( diff --git a/reconciler/example/main.go b/reconciler/example/main.go index 070301d0..4b0542f5 100644 --- a/reconciler/example/main.go +++ b/reconciler/example/main.go @@ -20,6 +20,7 @@ import ( "github.com/cilium/hive/cell" "github.com/cilium/hive/job" "github.com/cilium/statedb" + statedbhive "github.com/cilium/statedb/hive" "github.com/cilium/statedb/reconciler" ) @@ -107,7 +108,7 @@ var Hive = hive.NewWithOptions( }, }, - statedb.Cell, + statedbhive.Cell, job.Cell, cell.SimpleHealthCell, diff --git a/reconciler/go.mod b/reconciler/go.mod new file mode 100644 index 00000000..9bbcea5e --- /dev/null +++ b/reconciler/go.mod @@ -0,0 +1,50 @@ +module github.com/cilium/statedb/reconciler + +go 1.25 + +require ( + github.com/cilium/hive v1.0.0 + github.com/cilium/statedb v0.0.0 + github.com/cilium/statedb/hive v0.0.0 + github.com/spf13/cobra v1.8.0 + github.com/spf13/pflag v1.0.5 + github.com/stretchr/testify v1.8.4 + go.yaml.in/yaml/v3 v3.0.3 + golang.org/x/time v0.5.0 +) + +require ( + github.com/cilium/statedb/part v0.0.0 // indirect + github.com/cilium/stream v0.0.0-20240209152734-a0792b51812d // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/viper v1.18.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/dig v1.17.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/term v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.16.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace ( + github.com/cilium/statedb => ../ + github.com/cilium/statedb/hive => ../hive + github.com/cilium/statedb/part => ../part +) diff --git a/reconciler/go.sum b/reconciler/go.sum new file mode 100644 index 00000000..1ee06554 --- /dev/null +++ b/reconciler/go.sum @@ -0,0 +1,90 @@ +github.com/cilium/hive v1.0.0 h1:HEPvIFBCgAWOBclSVHoDXGpRB5SPgzMXdB54BOrITKE= +github.com/cilium/hive v1.0.0/go.mod h1:4/8FBMcTjVdkrNNWaB7t3QqaU4kZDJLJ1leKVP9GjEI= +github.com/cilium/stream v0.0.0-20240209152734-a0792b51812d h1:p6MgATaKEB9o7iAsk9rlzXNDMNCeKPAkx4Y8f+Zq8X8= +github.com/cilium/stream v0.0.0-20240209152734-a0792b51812d/go.mod h1:3VLiLgs8wfjirkuYqos4t0IBPQ+sXtf3tFkChLm6ARM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= +go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= +go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= +golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8= +golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/reconciler/helpers.go b/reconciler/helpers.go index 14e5b9f1..537bbd37 100644 --- a/reconciler/helpers.go +++ b/reconciler/helpers.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "slices" + "time" ) var closedWatchChannel = func() <-chan struct{} { @@ -26,6 +27,31 @@ func omittedError(n int) error { return fmt.Errorf("%d further errors omitted", n) } +func prettySince(t time.Time) string { + return prettyDuration(time.Since(t)) +} + +func prettyDuration(d time.Duration) string { + ago := float64(d) / float64(time.Microsecond) + if ago < 1000.0 { + return fmt.Sprintf("%.1fus", ago) + } + ago /= 1000.0 + if ago < 1000.0 { + return fmt.Sprintf("%.1fms", ago) + } + ago /= 1000.0 + if ago < 60.0 { + return fmt.Sprintf("%.1fs", ago) + } + ago /= 60.0 + if ago < 60.0 { + return fmt.Sprintf("%.1fm", ago) + } + ago /= 60.0 + return fmt.Sprintf("%.1fh", ago) +} + func joinErrors(errs []error) error { if len(errs) > maxJoinedErrors { errs = append(slices.Clone(errs)[:maxJoinedErrors], omittedError(len(errs)-maxJoinedErrors)) diff --git a/reconciler/multi_test.go b/reconciler/multi_test.go index 77fd0a92..c3094c85 100644 --- a/reconciler/multi_test.go +++ b/reconciler/multi_test.go @@ -18,6 +18,7 @@ import ( "github.com/cilium/hive/hivetest" "github.com/cilium/hive/job" "github.com/cilium/statedb" + statedbhive "github.com/cilium/statedb/hive" "github.com/cilium/statedb/index" "github.com/cilium/statedb/reconciler" "github.com/stretchr/testify/assert" @@ -93,7 +94,7 @@ func TestMultipleReconcilers(t *testing.T) { var db *statedb.DB hive := hive.New( - statedb.Cell, + statedbhive.Cell, job.Cell, cell.Provide( cell.NewSimpleHealth, diff --git a/reconciler/progress_test.go b/reconciler/progress_test.go index 173fecf4..018ce1f9 100644 --- a/reconciler/progress_test.go +++ b/reconciler/progress_test.go @@ -20,6 +20,7 @@ import ( "github.com/cilium/hive/hivetest" "github.com/cilium/hive/job" "github.com/cilium/statedb" + statedbhive "github.com/cilium/statedb/hive" "github.com/cilium/statedb/index" "github.com/cilium/statedb/reconciler" "github.com/stretchr/testify/require" @@ -123,7 +124,7 @@ func TestWaitUntilReconciled(t *testing.T) { ops := newWaitOps() hive := hive.New( - statedb.Cell, + statedbhive.Cell, job.Cell, cell.Provide( cell.NewSimpleHealth, diff --git a/reconciler/script_test.go b/reconciler/script_test.go index 174d7b07..d2a2ad39 100644 --- a/reconciler/script_test.go +++ b/reconciler/script_test.go @@ -23,6 +23,7 @@ import ( "github.com/cilium/hive/script" "github.com/cilium/hive/script/scripttest" "github.com/cilium/statedb" + statedbhive "github.com/cilium/statedb/hive" "github.com/cilium/statedb/index" "github.com/cilium/statedb/reconciler" "github.com/stretchr/testify/assert" @@ -45,7 +46,7 @@ func newEngine(t testing.TB, args []string) *script.Engine { expVarMetrics := reconciler.NewUnpublishedExpVarMetrics() hive := hive.New( - statedb.Cell, + statedbhive.Cell, job.Cell, cell.Provide( diff --git a/reconciler/types.go b/reconciler/types.go index 1f20624f..8ca90ca1 100644 --- a/reconciler/types.go +++ b/reconciler/types.go @@ -20,7 +20,6 @@ import ( "github.com/cilium/hive/job" "github.com/cilium/statedb" "github.com/cilium/statedb/index" - "github.com/cilium/statedb/internal" "go.yaml.in/yaml/v3" ) @@ -211,9 +210,9 @@ func (s Status) IsPendingOrRefreshing() bool { func (s Status) String() string { if s.Kind == StatusKindError { - return fmt.Sprintf("Error: %s (%s ago)", s.GetError(), internal.PrettySince(s.UpdatedAt)) + return fmt.Sprintf("Error: %s (%s ago)", s.GetError(), prettySince(s.UpdatedAt)) } - return fmt.Sprintf("%s (%s ago)", s.Kind, internal.PrettySince(s.UpdatedAt)) + return fmt.Sprintf("%s (%s ago)", s.Kind, prettySince(s.UpdatedAt)) } func (s Status) GetError() string { @@ -370,7 +369,7 @@ func (s StatusSet) String() string { b.WriteString(strings.Join(done, " ")) } b.WriteString(" (") - b.WriteString(internal.PrettySince(updatedAt)) + b.WriteString(prettySince(updatedAt)) b.WriteString(" ago)") return b.String() } diff --git a/release.sh b/release.sh new file mode 100755 index 00000000..034f5417 --- /dev/null +++ b/release.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# Release all modules at the same version. +# Usage: ./release.sh v1.2.0 +# +# This script: +# 1. Replaces replace directives and v0.0.0 placeholders with real versions +# 2. Runs go mod tidy on each module +# 3. Commits and tags each module in dependency order +# 4. Restores replace directives for development +# +# After running, push with: git push origin main $VERSION part/$VERSION hive/$VERSION reconciler/$VERSION + +set -euo pipefail + +VERSION=${1:?usage: release.sh VERSION (e.g. v1.2.0)} + +if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "error: VERSION must match vX.Y.Z" >&2 + exit 1 +fi + +if [ -n "$(git status --porcelain)" ]; then + echo "error: working tree is dirty, commit or stash changes first" >&2 + exit 1 +fi + +MODULE=github.com/cilium/statedb + +update_dep() { + local file=$1 dep=$2 version=$3 + sed -i '' "s|${dep} v[^ ]*|${dep} ${version}|g" "$file" +} + +remove_replace_block() { + local file=$1 + # Remove replace ( ... ) block + sed -i '' '/^replace ($/,/^)$/d' "$file" + # Remove single-line replace directives + sed -i '' '/^replace .* => \.\.\//d' "$file" +} + +# Step 1: Update go.mod files with real versions and remove replace directives. +echo "Updating go.mod files for ${VERSION}..." + +# Root: depends on part +update_dep go.mod "${MODULE}/part" "${VERSION}" +sed -i '' '/^replace.*statedb\/part/d' go.mod + +# Hive: depends on statedb, part +update_dep hive/go.mod "${MODULE}" "${VERSION}" +update_dep hive/go.mod "${MODULE}/part" "${VERSION}" +remove_replace_block hive/go.mod + +# Reconciler: depends on statedb, hive, part +update_dep reconciler/go.mod "${MODULE}" "${VERSION}" +update_dep reconciler/go.mod "${MODULE}/hive" "${VERSION}" +update_dep reconciler/go.mod "${MODULE}/part" "${VERSION}" +remove_replace_block reconciler/go.mod + +# Step 2: Tidy all modules (part first since others depend on it). +echo "Running go mod tidy..." +(cd part && go mod tidy) +go mod tidy +(cd hive && go mod tidy) +(cd reconciler && go mod tidy) + +# Step 3: Commit and tag in dependency order. +echo "Committing and tagging..." +git add go.mod go.sum part/go.mod part/go.sum hive/go.mod hive/go.sum reconciler/go.mod reconciler/go.sum +git commit -m "Release ${VERSION}" + +git tag "part/${VERSION}" +git tag "${VERSION}" +git tag "hive/${VERSION}" +git tag "reconciler/${VERSION}" + +# Step 4: Restore replace directives for development. +echo "Restoring replace directives..." +cat >> go.mod < ./part +EOF + +cat >> hive/go.mod < ../ + github.com/cilium/statedb/part => ../part +) +EOF + +cat >> reconciler/go.mod < ../ + github.com/cilium/statedb/hive => ../hive + github.com/cilium/statedb/part => ../part +) +EOF + +go mod tidy +(cd hive && go mod tidy) +(cd reconciler && go mod tidy) + +git add go.mod go.sum hive/go.mod hive/go.sum reconciler/go.mod reconciler/go.sum +git commit -m "Post-release: restore replace directives" + +echo "" +echo "Done. Push with:" +echo " git push origin main ${VERSION} part/${VERSION} hive/${VERSION} reconciler/${VERSION}" diff --git a/table.go b/table.go index c3b6dcd5..772b68bd 100644 --- a/table.go +++ b/table.go @@ -575,7 +575,7 @@ func (t *genTable[Obj]) Changes(txn WriteTxn) (ChangeIterator[Obj], error) { // anyChanges returns the anyChangeIterator. Used for implementing the /changes HTTP // API where we can't work with concrete object types as they're not known and thus // uninstantiatable. -func (t *genTable[Obj]) anyChanges(txn WriteTxn) (anyChangeIterator, error) { +func (t *genTable[Obj]) anyChanges(txn WriteTxn) (AnyChangeIterator, error) { iter, err := t.Changes(txn) if err != nil { return nil, err @@ -592,6 +592,10 @@ func (t *genTable[Obj]) typeName() string { return fmt.Sprintf("%T", zero) } +func (t *genTable[Obj]) NumDeletedObjects(txn ReadTxn) int { return t.numDeletedObjects(txn) } +func (t *genTable[Obj]) TypeName() string { return t.typeName() } +func (t *genTable[Obj]) AcquiredInfo() string { return t.getAcquiredInfo() } + func (t *genTable[Obj]) tableHeader() []string { return t.tableHeaderFunc() } diff --git a/types.go b/types.go index 0ec8414b..8fc5c31d 100644 --- a/types.go +++ b/types.go @@ -252,6 +252,16 @@ type TableMeta interface { // increments in a write transaction on each Insert and Delete. Revision(ReadTxn) Revision + // NumDeletedObjects returns the number of objects in the graveyard. + NumDeletedObjects(ReadTxn) int + + // TypeName returns the 'Obj' type as a string. + TypeName() string + + // AcquiredInfo returns information about the last write transaction + // against the table. + AcquiredInfo() string + // Internal unexported methods used only internally. tableInternal } @@ -389,7 +399,7 @@ type tableInternal interface { getIndexer(name string) *anyIndexer secondary() []anyIndexer // Secondary indexers (if any) sortableMutex() internal.SortableMutex // The sortable mutex for locking the table for writing - anyChanges(txn WriteTxn) (anyChangeIterator, error) + anyChanges(txn WriteTxn) (AnyChangeIterator, error) typeName() string // Returns the 'Obj' type as string unmarshalYAML(data []byte) (any, error) // Unmarshal the data into 'Obj' numDeletedObjects(txn ReadTxn) int // Number of objects in graveyard