From 9ec30ecc0c28b263cc4305d08dda7c3204dbc8f1 Mon Sep 17 00:00:00 2001 From: "M. J. Fromberger" Date: Mon, 11 Apr 2022 12:41:27 -0700 Subject: [PATCH] Add configuration diff tool. (#8298) * Add diff outputs as testdata. * Normalize all samples to kebabs. --- go.mod | 2 +- go.sum | 4 +- scripts/confix/condiff/condiff.go | 152 +++++++++++++++++++++++++ scripts/confix/testdata/diff-31-32.txt | 5 + scripts/confix/testdata/diff-32-33.txt | 6 + scripts/confix/testdata/diff-33-34.txt | 20 ++++ scripts/confix/testdata/diff-34-35.txt | 31 +++++ scripts/confix/testdata/diff-35-36.txt | 27 +++++ 8 files changed, 244 insertions(+), 3 deletions(-) create mode 100644 scripts/confix/condiff/condiff.go create mode 100644 scripts/confix/testdata/diff-31-32.txt create mode 100644 scripts/confix/testdata/diff-32-33.txt create mode 100644 scripts/confix/testdata/diff-33-34.txt create mode 100644 scripts/confix/testdata/diff-34-35.txt create mode 100644 scripts/confix/testdata/diff-35-36.txt diff --git a/go.mod b/go.mod index 60960e3bb..3101f26ad 100644 --- a/go.mod +++ b/go.mod @@ -73,7 +73,7 @@ require ( github.com/charithe/durationcheck v0.0.9 // indirect github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af // indirect github.com/containerd/continuity v0.2.1 // indirect - github.com/creachadair/tomledit v0.0.13 + github.com/creachadair/tomledit v0.0.15 github.com/daixiang0/gci v0.3.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/denis-tingaikin/go-header v0.4.3 // indirect diff --git a/go.sum b/go.sum index d1eb27611..0da80e4d6 100644 --- a/go.sum +++ b/go.sum @@ -225,8 +225,8 @@ github.com/creachadair/atomicfile v0.2.4 h1:GRjpQLmz/78I4+nBQpGMFrRa9yrL157AUTrA github.com/creachadair/atomicfile v0.2.4/go.mod h1:BRq8Une6ckFneYXZQ+kO7p1ZZP3I2fzVzf28JxrIkBc= github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6VqkYlkM= github.com/creachadair/taskgroup v0.3.2/go.mod h1:wieWwecHVzsidg2CsUnFinW1faVN4+kq+TDlRJQ0Wbk= -github.com/creachadair/tomledit v0.0.13 h1:UKsFVZbCKH/8vIh50cDEtRu2KuBync5v1WXMYb+HSJ0= -github.com/creachadair/tomledit v0.0.13/go.mod h1:gvtfnSZLa+YNQD28vaPq0Nk12bRxEhmUdBzAWn+EGF4= +github.com/creachadair/tomledit v0.0.15 h1:g/qlpwSFIXEngyz8gcTEGZtvdGBLHODwV+Z0BFILVxw= +github.com/creachadair/tomledit v0.0.15/go.mod h1:gvtfnSZLa+YNQD28vaPq0Nk12bRxEhmUdBzAWn+EGF4= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= diff --git a/scripts/confix/condiff/condiff.go b/scripts/confix/condiff/condiff.go new file mode 100644 index 000000000..6b11e4e2c --- /dev/null +++ b/scripts/confix/condiff/condiff.go @@ -0,0 +1,152 @@ +// Program condiff performs a keyspace diff on two TOML documents. +package main + +import ( + "context" + "flag" + "fmt" + "io" + "log" + "os" + "path/filepath" + "sort" + "strings" + + "github.com/creachadair/tomledit" + "github.com/creachadair/tomledit/parser" + "github.com/creachadair/tomledit/transform" +) + +var ( + doDesnake = flag.Bool("desnake", false, "Convert snake_case to kebab-case before comparing") +) + +func init() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, `Usage: %[1]s [options] f1 f2 + +Diff the keyspaces of the TOML documents in files f1 and f2. +The output prints one line per key that differs: + + -S name -- section exists in f1 but not f2 + +S name -- section exists in f2 but not f1 + -M name -- mapping exists in f1 but not f2 + +M name -- mapping exists in f2 but not f1 + +Comments, order, and values are ignored for comparison purposes. + +Options: +`, filepath.Base(os.Args[0])) + flag.PrintDefaults() + } +} + +func main() { + flag.Parse() + + if flag.NArg() != 2 { + log.Fatalf("Usage: %[1]s ", filepath.Base(os.Args[0])) + } + lhs := mustParse(flag.Arg(0)) + rhs := mustParse(flag.Arg(1)) + if *doDesnake { + log.Printf("Converting all names from snake_case to kebab-case") + fix := transform.SnakeToKebab() + _ = fix(context.Background(), lhs) + _ = fix(context.Background(), rhs) + } + diffDocs(os.Stdout, lhs, rhs) +} + +func mustParse(path string) *tomledit.Document { + f, err := os.Open(path) + if err != nil { + log.Fatalf("Opening TOML input: %v", err) + } + defer f.Close() + doc, err := tomledit.Parse(f) + if err != nil { + log.Fatalf("Parsing %q: %v", path, err) + } + return doc +} + +func allKeys(s *tomledit.Section) []string { + var keys []string + s.Scan(func(key parser.Key, _ *tomledit.Entry) bool { + keys = append(keys, key.String()) + return true + }) + return keys +} + +const ( + delSection = "-S" + delMapping = "-M" + addSection = "+S" + addMapping = "+M" + + delMapSep = "\n" + delMapping + " " + addMapSep = "\n" + addMapping + " " +) + +func diffDocs(w io.Writer, lhs, rhs *tomledit.Document) { + diffSections(w, lhs.Global, rhs.Global) + lsec, rsec := lhs.Sections, rhs.Sections + transform.SortSectionsByName(lsec) + transform.SortSectionsByName(rsec) + + i, j := 0, 0 + for i < len(lsec) && j < len(rsec) { + if lsec[i].Name.Before(rsec[j].Name) { + fmt.Fprintln(w, delSection, lsec[i].Name) + fmt.Fprintln(w, delMapping, strings.Join(allKeys(lsec[i]), delMapSep)) + i++ + } else if rsec[j].Name.Before(lsec[i].Name) { + fmt.Fprintln(w, addSection, rsec[j].Name) + fmt.Fprintln(w, addMapping, strings.Join(allKeys(rsec[j]), addMapSep)) + j++ + } else { + diffSections(w, lsec[i], rsec[j]) + i++ + j++ + } + } + for ; i < len(lsec); i++ { + fmt.Fprintln(w, delSection, lsec[i].Name) + fmt.Fprintln(w, delMapping, strings.Join(allKeys(lsec[i]), delMapSep)) + } + for ; j < len(rsec); j++ { + fmt.Fprintln(w, addSection, rsec[j].Name) + fmt.Fprintln(w, addMapping, strings.Join(allKeys(rsec[j]), addMapSep)) + } +} + +func diffSections(w io.Writer, lhs, rhs *tomledit.Section) { + diffKeys(w, allKeys(lhs), allKeys(rhs)) +} + +func diffKeys(w io.Writer, lhs, rhs []string) { + sort.Strings(lhs) + sort.Strings(rhs) + + i, j := 0, 0 + for i < len(lhs) && j < len(rhs) { + if lhs[i] < rhs[j] { + fmt.Fprintln(w, delMapping, lhs[i]) + i++ + } else if lhs[i] > rhs[j] { + fmt.Fprintln(w, addMapping, rhs[j]) + j++ + } else { + i++ + j++ + } + } + for ; i < len(lhs); i++ { + fmt.Fprintln(w, delMapping, lhs[i]) + } + for ; j < len(rhs); j++ { + fmt.Fprintln(w, addMapping, rhs[j]) + } +} diff --git a/scripts/confix/testdata/diff-31-32.txt b/scripts/confix/testdata/diff-31-32.txt new file mode 100644 index 000000000..98855bade --- /dev/null +++ b/scripts/confix/testdata/diff-31-32.txt @@ -0,0 +1,5 @@ ++S fastsync ++M fastsync.version ++M mempool.max-tx-bytes ++M rpc.max-body-bytes ++M rpc.max-header-bytes diff --git a/scripts/confix/testdata/diff-32-33.txt b/scripts/confix/testdata/diff-32-33.txt new file mode 100644 index 000000000..7aa61856a --- /dev/null +++ b/scripts/confix/testdata/diff-32-33.txt @@ -0,0 +1,6 @@ ++M p2p.persistent-peers-max-dial-period ++M p2p.unconditional-peer-ids ++M tx-index.index-all-keys +-M tx-index.index-all-tags ++M tx-index.index-keys +-M tx-index.index-tags diff --git a/scripts/confix/testdata/diff-33-34.txt b/scripts/confix/testdata/diff-33-34.txt new file mode 100644 index 000000000..a0ac7a98d --- /dev/null +++ b/scripts/confix/testdata/diff-33-34.txt @@ -0,0 +1,20 @@ +-M prof-laddr ++M consensus.double-sign-check-height ++M mempool.keep-invalid-txs-in-cache ++M mempool.max-batch-bytes ++M rpc.experimental-close-on-slow-client ++M rpc.experimental-subscription-buffer-size ++M rpc.experimental-websocket-write-buffer-size ++M rpc.pprof-laddr ++S statesync ++M statesync.enable ++M statesync.rpc-servers ++M statesync.trust-height ++M statesync.trust-hash ++M statesync.trust-period ++M statesync.discovery-time ++M statesync.temp-dir ++M statesync.chunk-request-timeout ++M statesync.chunk-fetchers +-M tx-index.index-all-keys +-M tx-index.index-keys diff --git a/scripts/confix/testdata/diff-34-35.txt b/scripts/confix/testdata/diff-34-35.txt new file mode 100644 index 000000000..13a4432a0 --- /dev/null +++ b/scripts/confix/testdata/diff-34-35.txt @@ -0,0 +1,31 @@ +-M fast-sync ++M mode +-M priv-validator-key-file +-M priv-validator-laddr +-M priv-validator-state-file ++S blocksync ++M blocksync.enable ++M blocksync.version +-S fastsync +-M fastsync.version ++M mempool.ttl-duration ++M mempool.ttl-num-blocks ++M mempool.version +-M mempool.wal-dir ++M p2p.bootstrap-peers ++M p2p.max-connections ++M p2p.max-incoming-connection-attempts ++M p2p.queue-type +-M p2p.seed-mode ++M p2p.use-legacy ++S priv-validator ++M priv-validator.key-file ++M priv-validator.state-file ++M priv-validator.laddr ++M priv-validator.client-certificate-file ++M priv-validator.client-key-file ++M priv-validator.root-ca-file +-M statesync.chunk-fetchers ++M statesync.fetchers ++M statesync.use-p2p ++M tx-index.psql-conn diff --git a/scripts/confix/testdata/diff-35-36.txt b/scripts/confix/testdata/diff-35-36.txt new file mode 100644 index 000000000..13fd268af --- /dev/null +++ b/scripts/confix/testdata/diff-35-36.txt @@ -0,0 +1,27 @@ +-S blocksync +-M blocksync.enable +-M blocksync.version +-M consensus.skip-timeout-commit +-M consensus.timeout-commit +-M consensus.timeout-precommit +-M consensus.timeout-precommit-delta +-M consensus.timeout-prevote +-M consensus.timeout-prevote-delta +-M consensus.timeout-propose +-M consensus.timeout-propose-delta +-M mempool.version +-M p2p.addr-book-file +-M p2p.addr-book-strict +-M p2p.max-num-inbound-peers +-M p2p.max-num-outbound-peers +-M p2p.persistent-peers-max-dial-period +-M p2p.unconditional-peer-ids +-M p2p.use-legacy ++M rpc.event-log-max-items ++M rpc.event-log-window-size +-M rpc.experimental-close-on-slow-client ++M rpc.experimental-disable-websocket +-M rpc.experimental-subscription-buffer-size +-M rpc.experimental-websocket-write-buffer-size +-M rpc.grpc-laddr +-M rpc.grpc-max-open-connections