Skip to content

Commit b8c24bf

Browse files
renaynayWondertan
andauthored
feat(cmd): Add config-remove and config-update commands to CLI (#2068)
This PR supersedes #1481 as it was outdated (my fault for not addressing earlier) and also simplifies the `config-update` command. It introduces two new commands: * `config-remove` which will delete a node's config * `config-update` which will update an outdated config by loading it, applying any newly added fields, and saving it back in the config path. There is one unfortunate caveat which is that if a newly added field is merged into the old config, some old custom values from that field may be overwritten which is why I included a prompt to the user to ensure their old custom values were preserved. Custom values from other fields that were unchanged in the config are preserved however. If there's time, I will investigate how to preserve custom values of a specific field that has been upgraded. Credit to @dixitaniket for implementing this feature. Closes #1414 --------- Co-authored-by: Hlib Kanunnikov <[email protected]>
1 parent be3a165 commit b8c24bf

File tree

8 files changed

+227
-0
lines changed

8 files changed

+227
-0
lines changed

cmd/celestia/bridge.go

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ func init() {
3232
cmdnode.Start(flags...),
3333
cmdnode.AuthCmd(flags...),
3434
cmdnode.ResetStore(flags...),
35+
cmdnode.RemoveConfigCmd(flags...),
36+
cmdnode.UpdateConfigCmd(flags...),
3537
)
3638
}
3739

cmd/celestia/full.go

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ func init() {
3636
cmdnode.Start(flags...),
3737
cmdnode.AuthCmd(flags...),
3838
cmdnode.ResetStore(flags...),
39+
cmdnode.RemoveConfigCmd(flags...),
40+
cmdnode.UpdateConfigCmd(flags...),
3941
)
4042
}
4143

cmd/celestia/light.go

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ func init() {
3636
cmdnode.Start(flags...),
3737
cmdnode.AuthCmd(flags...),
3838
cmdnode.ResetStore(flags...),
39+
cmdnode.RemoveConfigCmd(flags...),
40+
cmdnode.UpdateConfigCmd(flags...),
3941
)
4042
}
4143

cmd/config.go

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package cmd
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
flag "github.com/spf13/pflag"
6+
7+
"github.com/celestiaorg/celestia-node/nodebuilder"
8+
)
9+
10+
func RemoveConfigCmd(fsets ...*flag.FlagSet) *cobra.Command {
11+
cmd := &cobra.Command{
12+
Use: "config-remove",
13+
Short: "Deletes the node's config",
14+
Args: cobra.NoArgs,
15+
RunE: func(cmd *cobra.Command, args []string) error {
16+
ctx := cmd.Context()
17+
return nodebuilder.RemoveConfig(StorePath(ctx))
18+
},
19+
}
20+
21+
for _, set := range fsets {
22+
cmd.Flags().AddFlagSet(set)
23+
}
24+
return cmd
25+
}
26+
27+
func UpdateConfigCmd(fsets ...*flag.FlagSet) *cobra.Command {
28+
cmd := &cobra.Command{
29+
Use: "config-update",
30+
Short: "Updates the node's outdated config",
31+
Long: "Updates the node's outdated config with default values from newly-added fields. Check the config " +
32+
" afterwards to ensure all old custom values were preserved.",
33+
Args: cobra.NoArgs,
34+
RunE: func(cmd *cobra.Command, args []string) error {
35+
ctx := cmd.Context()
36+
return nodebuilder.UpdateConfig(NodeType(ctx), StorePath(ctx))
37+
},
38+
}
39+
40+
for _, set := range fsets {
41+
cmd.Flags().AddFlagSet(set)
42+
}
43+
return cmd
44+
}

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ require (
2828
github.com/gorilla/mux v1.8.0
2929
github.com/hashicorp/go-retryablehttp v0.7.2
3030
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
31+
github.com/imdario/mergo v0.3.15
3132
github.com/ipfs/go-blockservice v0.5.0
3233
github.com/ipfs/go-cid v0.3.2
3334
github.com/ipfs/go-datastore v0.6.0

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,8 @@ github.com/iancoleman/orderedmap v0.1.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36
917917
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
918918
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
919919
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo=
920+
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
921+
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
920922
github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ=
921923
github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8=
922924
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=

nodebuilder/config.go

+72
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55
"os"
66

77
"github.com/BurntSushi/toml"
8+
"github.com/imdario/mergo"
89

10+
"github.com/celestiaorg/celestia-node/libs/fslock"
911
"github.com/celestiaorg/celestia-node/nodebuilder/core"
1012
"github.com/celestiaorg/celestia-node/nodebuilder/das"
1113
"github.com/celestiaorg/celestia-node/nodebuilder/gateway"
@@ -80,6 +82,76 @@ func LoadConfig(path string) (*Config, error) {
8082
return &cfg, cfg.Decode(f)
8183
}
8284

85+
// RemoveConfig removes the Config from the given store path.
86+
func RemoveConfig(path string) (err error) {
87+
path, err = storePath(path)
88+
if err != nil {
89+
return
90+
}
91+
92+
flock, err := fslock.Lock(lockPath(path))
93+
if err != nil {
94+
if err == fslock.ErrLocked {
95+
err = ErrOpened
96+
}
97+
return
98+
}
99+
defer flock.Unlock() //nolint: errcheck
100+
101+
return removeConfig(configPath(path))
102+
}
103+
104+
// removeConfig removes Config from the given 'path'.
105+
func removeConfig(path string) error {
106+
return os.Remove(path)
107+
}
108+
109+
// UpdateConfig loads the node's config and applies new values
110+
// from the default config of the given node type, saving the
111+
// newly updated config into the node's config path.
112+
func UpdateConfig(tp node.Type, path string) (err error) {
113+
path, err = storePath(path)
114+
if err != nil {
115+
return
116+
}
117+
118+
flock, err := fslock.Lock(lockPath(path))
119+
if err != nil {
120+
if err == fslock.ErrLocked {
121+
err = ErrOpened
122+
}
123+
return
124+
}
125+
defer flock.Unlock() //nolint: errcheck
126+
127+
newCfg := DefaultConfig(tp)
128+
129+
cfgPath := configPath(path)
130+
cfg, err := LoadConfig(cfgPath)
131+
if err != nil {
132+
return
133+
}
134+
135+
cfg, err = updateConfig(cfg, newCfg)
136+
if err != nil {
137+
return
138+
}
139+
140+
// save the updated config
141+
err = removeConfig(cfgPath)
142+
if err != nil {
143+
return
144+
}
145+
return SaveConfig(cfgPath, cfg)
146+
}
147+
148+
// updateConfig merges new values from the new config into the old
149+
// config, returning the updated old config.
150+
func updateConfig(oldCfg *Config, newCfg *Config) (*Config, error) {
151+
err := mergo.Merge(oldCfg, newCfg, mergo.WithOverrideEmptySlice)
152+
return oldCfg, err
153+
}
154+
83155
// TODO(@Wondertan): We should have a description for each field written into w,
84156
// so users can instantly understand purpose of each field. Ideally, we should have a utility
85157
// program to parse comments from actual sources(*.go files) and generate docs from comments.

nodebuilder/config_test.go

+102
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"testing"
66

7+
"github.com/BurntSushi/toml"
78
"github.com/stretchr/testify/assert"
89
"github.com/stretchr/testify/require"
910

@@ -33,3 +34,104 @@ func TestConfigWriteRead(t *testing.T) {
3334
})
3435
}
3536
}
37+
38+
// TestUpdateConfig tests that updating an outdated config
39+
// using a new default config applies the correct values and
40+
// preserves old custom values.
41+
func TestUpdateConfig(t *testing.T) {
42+
cfg := new(Config)
43+
_, err := toml.Decode(outdatedConfig, cfg)
44+
require.NoError(t, err)
45+
46+
newCfg := DefaultConfig(node.Light)
47+
// ensure this config field is not filled in the outdated config
48+
require.NotEqual(t, newCfg.Share.PeerManagerParams, cfg.Share.PeerManagerParams)
49+
50+
cfg, err = updateConfig(cfg, newCfg)
51+
require.NoError(t, err)
52+
// ensure this config field is now set after updating the config
53+
require.Equal(t, newCfg.Share.PeerManagerParams, cfg.Share.PeerManagerParams)
54+
// ensure old custom values were not changed
55+
require.Equal(t, "thisshouldnthavechanged", cfg.State.KeyringAccName)
56+
require.Equal(t, "7979", cfg.RPC.Port)
57+
require.True(t, cfg.Gateway.Enabled)
58+
}
59+
60+
// outdatedConfig is an outdated config from a light node
61+
var outdatedConfig = `
62+
[Core]
63+
IP = "0.0.0.0"
64+
RPCPort = "0"
65+
GRPCPort = "0"
66+
67+
[State]
68+
KeyringAccName = "thisshouldnthavechanged"
69+
KeyringBackend = "test"
70+
71+
[P2P]
72+
ListenAddresses = ["/ip4/0.0.0.0/udp/2121/quic-v1", "/ip6/::/udp/2121/quic-v1", "/ip4/0.0.0.0/tcp/2121",
73+
"/ip6/::/tcp/2121"]
74+
AnnounceAddresses = []
75+
NoAnnounceAddresses = ["/ip4/0.0.0.0/udp/2121/quic-v1", "/ip4/127.0.0.1/udp/2121/quic-v1", "/ip6/::/udp/2121/quic-v1",
76+
"/ip4/0.0.0.0/tcp/2121", "/ip4/127.0.0.1/tcp/2121", "/ip6/::/tcp/2121"]
77+
MutualPeers = []
78+
PeerExchange = false
79+
RoutingTableRefreshPeriod = "1m0s"
80+
[P2P.ConnManager]
81+
Low = 50
82+
High = 100
83+
GracePeriod = "1m0s"
84+
[P2P.Metrics]
85+
PrometheusAgentPort = "8890"
86+
87+
[RPC]
88+
Address = "0.0.0.0"
89+
Port = "7979"
90+
91+
[Gateway]
92+
Address = "0.0.0.0"
93+
Port = "26659"
94+
Enabled = true
95+
96+
[Share]
97+
PeersLimit = 5
98+
DiscoveryInterval = "30s"
99+
AdvertiseInterval = "30s"
100+
UseShareExchange = true
101+
[Share.ShrExEDSParams]
102+
ServerReadTimeout = "5s"
103+
ServerWriteTimeout = "1m0s"
104+
HandleRequestTimeout = "1m0s"
105+
ConcurrencyLimit = 10
106+
BufferSize = 32768
107+
[Share.ShrExNDParams]
108+
ServerReadTimeout = "5s"
109+
ServerWriteTimeout = "2m35s"
110+
HandleRequestTimeout = "1m0s"
111+
ConcurrencyLimit = 10
112+
113+
[Header]
114+
TrustedHash = ""
115+
TrustedPeers = []
116+
[Header.Store]
117+
StoreCacheSize = 4096
118+
IndexCacheSize = 16384
119+
WriteBatchSize = 2048
120+
[Header.Syncer]
121+
TrustingPeriod = "168h0m0s"
122+
[Header.Server]
123+
WriteDeadline = "8s"
124+
ReadDeadline = "1m0s"
125+
RangeRequestTimeout = "10s"
126+
[Header.Client]
127+
MaxHeadersPerRangeRequest = 64
128+
RangeRequestTimeout = "8s"
129+
TrustedPeersRequestTimeout = "300ms"
130+
131+
[DASer]
132+
SamplingRange = 100
133+
ConcurrencyLimit = 16
134+
BackgroundStoreInterval = "10m0s"
135+
SampleFrom = 1
136+
SampleTimeout = "4m0s"
137+
`

0 commit comments

Comments
 (0)