Skip to content

Commit

Permalink
Refactor to use the new flags system in script
Browse files Browse the repository at this point in the history
Use the new pflag-based system for specifying flags for
script commands.

Signed-off-by: Jussi Maki <[email protected]>
  • Loading branch information
joamaki committed Dec 13, 2024
1 parent b234be5 commit d0cce89
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 123 deletions.
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ module github.com/cilium/statedb

go 1.23

replace (
github.com/cilium/hive => ../hive
)

require (
github.com/cilium/hive v0.0.0-20241025140746-d66ad09f4384
github.com/cilium/stream v0.0.0-20240209152734-a0792b51812d
Expand Down
10 changes: 5 additions & 5 deletions reconciler/testdata/batching.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ health 'job-reconcile.*level=OK.*message=OK, 0 object'

# Check metrics
expvar
! grep 'reconciliation_count.test: 0$'
grep 'reconciliation_current_errors.test: 0$'
! grep 'reconciliation_total_errors.test: 0$'
! grep 'reconciliation_duration.test/update: 0$'
! grep 'reconciliation_duration.test/delete: 0$'
! stdout 'reconciliation_count.test: 0$'
stdout 'reconciliation_current_errors.test: 0$'
! stdout 'reconciliation_total_errors.test: 0$'
! stdout 'reconciliation_duration.test/update: 0$'
! stdout 'reconciliation_duration.test/delete: 0$'

# ------------

Expand Down
10 changes: 5 additions & 5 deletions reconciler/testdata/incremental.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ health 'job-reconcile.*level=OK.*message=OK, 0 object'

# Check metrics
expvar
! grep 'reconciliation_count.test: 0$'
grep 'reconciliation_current_errors.test: 0$'
! grep 'reconciliation_total_errors.test: 0$'
! grep 'reconciliation_duration.test/update: 0$'
! grep 'reconciliation_duration.test/delete: 0$'
! stdout 'reconciliation_count.test: 0$'
stdout 'reconciliation_current_errors.test: 0$'
! stdout 'reconciliation_total_errors.test: 0$'
! stdout 'reconciliation_duration.test/update: 0$'
! stdout 'reconciliation_duration.test/delete: 0$'

# ------------

Expand Down
24 changes: 12 additions & 12 deletions reconciler/testdata/pruning.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,23 @@ mark-init
expect-ops prune(n=2)
health 'job-reconcile.*level=OK'
expvar
! grep 'prune_count.test: 0'
! stdout 'prune_count.test: 0'

# Pruning with faulty ops will mark status as degraded
set-faulty true
prune
expect-ops 'prune(n=2) fail'
health 'job-reconcile.*level=Degraded.*message=.*prune fail'
expvar
grep 'prune_current_errors.test: 1'
stdout 'prune_current_errors.test: 1'

# Pruning again with healthy ops fixes the status.
set-faulty false
prune
expect-ops 'prune(n=2)'
health 'job-reconcile.*level=OK'
expvar
grep 'prune_current_errors.test: 0'
stdout 'prune_current_errors.test: 0'

# Delete an object and check pruning happens without it
db/delete test-objects obj1.yaml
Expand All @@ -44,15 +44,15 @@ expect-ops prune(n=0) delete(2) prune(n=1)

# Check metrics
expvar
! grep 'prune_count.test: 0'
grep 'prune_current_errors.test: 0'
grep 'prune_total_errors.test: 1'
! grep 'prune_duration.test: 0$'
! grep 'reconciliation_count.test: 0$'
grep 'reconciliation_current_errors.test: 0$'
grep 'reconciliation_total_errors.test: 0$'
! grep 'reconciliation_duration.test/update: 0$'
! grep 'reconciliation_duration.test/delete: 0$'
! stdout 'prune_count.test: 0'
stdout 'prune_current_errors.test: 0'
stdout 'prune_total_errors.test: 1'
! stdout 'prune_duration.test: 0$'
! stdout 'reconciliation_count.test: 0$'
stdout 'reconciliation_current_errors.test: 0$'
stdout 'reconciliation_total_errors.test: 0$'
! stdout 'reconciliation_duration.test/update: 0$'
! stdout 'reconciliation_duration.test/delete: 0$'

-- obj1.yaml --
id: 1
Expand Down
144 changes: 80 additions & 64 deletions script.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package statedb
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"iter"
Expand All @@ -19,6 +18,7 @@ import (
"github.com/cilium/hive"

Check failure on line 18 in script.go

View workflow job for this annotation

GitHub Actions / test

github.com/cilium/[email protected]: replacement directory ../hive does not exist

Check failure on line 18 in script.go

View workflow job for this annotation

GitHub Actions / test

github.com/cilium/[email protected]: replacement directory ../hive does not exist
"github.com/cilium/hive/script"

Check failure on line 19 in script.go

View workflow job for this annotation

GitHub Actions / test

github.com/cilium/[email protected]: replacement directory ../hive does not exist

Check failure on line 19 in script.go

View workflow job for this annotation

GitHub Actions / test

github.com/cilium/[email protected]: replacement directory ../hive does not exist
"github.com/liggitt/tabwriter"
"github.com/spf13/pflag"
"golang.org/x/time/rate"
"gopkg.in/yaml.v3"
)
Expand Down Expand Up @@ -90,17 +90,14 @@ func DBCmd(db *DB) script.Cmd {
)
}

func newCmdFlagSet(w io.Writer) *flag.FlagSet {
fs := flag.NewFlagSet("", flag.ContinueOnError)
fs.SetOutput(w)
return fs
}

func InitializedCmd(db *DB) script.Cmd {
return script.Command(
script.CmdUsage{
Summary: "Wait until all or specific tables have been initialized",
Args: "[-timeout=<duration>] table...",
Args: "table...",
Flags: func(fs *pflag.FlagSet) {
fs.Duration("timeout", 5*time.Second, "Maximum amount of time to wait for the table contents to match")
},
Detail: []string{
"Waits until all or specific tables have been marked",
"initialized. The default timeout is 5 seconds.",
Expand All @@ -111,15 +108,13 @@ func InitializedCmd(db *DB) script.Cmd {
},
},
func(s *script.State, args ...string) (script.WaitFunc, error) {
flags := newCmdFlagSet(s.LogWriter())
timeout := flags.Duration("timeout", 5*time.Second, "Maximum amount of time to wait for the table contents to match")
if err := flags.Parse(args); err != nil {
return nil, fmt.Errorf("%w: %w", script.ErrUsage, err)
timeout, err := s.Flags.GetDuration("timeout")
if err != nil {
return nil, err
}
args = flags.Args()

txn := db.ReadTxn()
timeoutChan := time.After(*timeout)
timeoutChan := time.After(timeout)
allTbls := db.GetTables(txn)
tbls := allTbls

Expand Down Expand Up @@ -166,7 +161,12 @@ func ShowCmd(db *DB) script.Cmd {
return script.Command(
script.CmdUsage{
Summary: "Show the contents of a table",
Args: "[-o=<file>] [-columns=col1,...] [-format={table,yaml,json}] table",
Args: "table",
Flags: func(fs *pflag.FlagSet) {
fs.StringP("out", "o", "", "File to write to instead of stdout")
fs.StringSlice("columns", nil, "Columns to write")
fs.StringP("format", "f", "table", "Format to write in (table, yaml or json)")
},
Detail: []string{
"Show the contents of a table.",
"",
Expand All @@ -182,33 +182,32 @@ func ShowCmd(db *DB) script.Cmd {
},
},
func(s *script.State, args ...string) (script.WaitFunc, error) {
flags := newCmdFlagSet(s.LogWriter())
file := flags.String("o", "", "File to write to instead of stdout")
columns := flags.String("columns", "", "Comma-separated list of columns to write")
format := flags.String("format", "table", "Format to write in (table, yaml, json)")
if err := flags.Parse(args); err != nil {
return nil, fmt.Errorf("%w: %w", script.ErrUsage, err)
file, err := s.Flags.GetString("out")
if err != nil {
return nil, err
}

var cols []string
if len(*columns) > 0 {
cols = strings.Split(*columns, ",")
columns, err := s.Flags.GetStringSlice("columns")
if err != nil {
return nil, err
}
format, err := s.Flags.GetString("format")
if err != nil {
return nil, err
}

args = flags.Args()
if len(args) < 1 {
return nil, fmt.Errorf("missing table name")
}
tableName := args[0]
return func(*script.State) (stdout, stderr string, err error) {
var buf strings.Builder
var w io.Writer
if *file == "" {
if file == "" {
w = &buf
} else {
f, err := os.OpenFile(s.Path(*file), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
f, err := os.OpenFile(s.Path(file), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
return "", "", fmt.Errorf("OpenFile(%s): %w", *file, err)
return "", "", fmt.Errorf("OpenFile(%s): %w", file, err)
}
defer f.Close()
w = f
Expand All @@ -217,7 +216,7 @@ func ShowCmd(db *DB) script.Cmd {
if err != nil {
return "", "", err
}
err = writeObjects(tbl, tbl.All(txn), w, cols, *format)
err = writeObjects(tbl, tbl.All(txn), w, columns, format)
return buf.String(), "", err
}, nil
})
Expand All @@ -227,7 +226,11 @@ func CompareCmd(db *DB) script.Cmd {
return script.Command(
script.CmdUsage{
Summary: "Compare table",
Args: "[-timeout=<dur>] [-grep=<pattern>] table file",
Args: "table file",
Flags: func(fs *pflag.FlagSet) {
fs.Duration("timeout", 5*time.Second, "Maximum amount of time to wait for the table contents to match")
fs.String("grep", "", "Grep the result rows and only compare matching ones")
},
Detail: []string{
"Compare the contents of a table against a file.",
"The comparison is retried until a timeout (1s default).",
Expand All @@ -243,21 +246,22 @@ func CompareCmd(db *DB) script.Cmd {
},
},
func(s *script.State, args ...string) (script.WaitFunc, error) {
flags := newCmdFlagSet(s.LogWriter())
timeout := flags.Duration("timeout", time.Second, "Maximum amount of time to wait for the table contents to match")
grep := flags.String("grep", "", "Grep the result rows and only compare matching ones")
err := flags.Parse(args)
timeout, err := s.Flags.GetDuration("timeout")
if err != nil {
return nil, err
}
grep, err := s.Flags.GetString("grep")
if err != nil {
return nil, fmt.Errorf("%w: %w", script.ErrUsage, err)
return nil, err
}
args = flags.Args()

if len(args) != 2 {
return nil, fmt.Errorf("expected table and filename")
}

var grepRe *regexp.Regexp
if *grep != "" {
grepRe, err = regexp.Compile(*grep)
if grep != "" {
grepRe, err = regexp.Compile(grep)
if err != nil {
return nil, fmt.Errorf("bad grep: %w", err)
}
Expand Down Expand Up @@ -292,7 +296,7 @@ func CompareCmd(db *DB) script.Cmd {
}
lines = lines[1:]
origLines := lines
timeoutChan := time.After(*timeout)
timeoutChan := time.After(timeout)

for {
lines = origLines
Expand Down Expand Up @@ -485,8 +489,15 @@ func queryCmd(db *DB, query int, summary string, detail []string) script.Cmd {
return script.Command(
script.CmdUsage{
Summary: summary,
Args: "[-o=<file>] [-columns=col1,...] [-format={table*,yaml,json}] [-index=<index>] table key",
Detail: detail,
Args: "table key",
Flags: func(fs *pflag.FlagSet) {
fs.StringP("out", "o", "", "File to write to instead of stdout")
fs.StringSlice("columns", nil, "Columns to write")
fs.StringP("format", "f", "table", "Format to write in (table, yaml or json)")
fs.StringP("index", "i", "", "Index to query")
fs.Bool("delete", false, "Delete all matching objects")
},
Detail: detail,
},
func(s *script.State, args ...string) (script.WaitFunc, error) {
return runQueryCmd(query, db, s, args)
Expand All @@ -495,22 +506,27 @@ 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) {
flags := newCmdFlagSet(s.LogWriter())
file := flags.String("o", "", "File to write results to instead of stdout")
index := flags.String("index", "", "Index to query")
format := flags.String("format", "table", "Format to write in (table, yaml, json)")
columns := flags.String("columns", "", "Comma-separated list of columns to write")
delete := flags.Bool("delete", false, "Delete all matching objects")
if err := flags.Parse(args); err != nil {
return nil, fmt.Errorf("%w: %w", script.ErrUsage, err)
file, err := s.Flags.GetString("out")
if err != nil {
return nil, err
}

var cols []string
if len(*columns) > 0 {
cols = strings.Split(*columns, ",")
columns, err := s.Flags.GetStringSlice("columns")
if err != nil {
return nil, err
}
format, err := s.Flags.GetString("format")
if err != nil {
return nil, err
}
index, err := s.Flags.GetString("index")
if err != nil {
return nil, err
}
delete, err := s.Flags.GetBool("delete")
if err != nil {
return nil, err
}

args = flags.Args()
if len(args) < 2 {
return nil, fmt.Errorf("expected table and key")
}
Expand All @@ -523,12 +539,12 @@ func runQueryCmd(query int, db *DB, s *script.State, args []string) (script.Wait

var buf strings.Builder
var w io.Writer
if *file == "" {
if file == "" {
w = &buf
} else {
f, err := os.OpenFile(s.Path(*file), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
f, err := os.OpenFile(s.Path(file), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
return "", "", fmt.Errorf("OpenFile(%s): %s", *file, err)
return "", "", fmt.Errorf("OpenFile(%s): %s", file, err)
}
defer f.Close()
w = f
Expand All @@ -537,13 +553,13 @@ func runQueryCmd(query int, db *DB, s *script.State, args []string) (script.Wait
var it iter.Seq2[any, uint64]
switch query {
case queryCmdList:
it, err = tbl.List(txn, *index, args[1])
it, err = tbl.List(txn, index, args[1])
case queryCmdLowerBound:
it, err = tbl.LowerBound(txn, *index, args[1])
it, err = tbl.LowerBound(txn, index, args[1])
case queryCmdPrefix:
it, err = tbl.Prefix(txn, *index, args[1])
it, err = tbl.Prefix(txn, index, args[1])
case queryCmdGet:
it, err = tbl.List(txn, *index, args[1])
it, err = tbl.List(txn, index, args[1])
if err == nil {
it = firstOfSeq2(it)
}
Expand All @@ -554,12 +570,12 @@ func runQueryCmd(query int, db *DB, s *script.State, args []string) (script.Wait
return "", "", fmt.Errorf("query: %w", err)
}

err = writeObjects(tbl, it, w, cols, *format)
err = writeObjects(tbl, it, w, columns, format)
if err != nil {
return "", "", err
}

if *delete {
if delete {
wtxn := db.WriteTxn(tbl.Meta)
count := 0
for obj := range it {
Expand Down
Loading

0 comments on commit d0cce89

Please sign in to comment.