feat(cmds): allow to set the configuration file path
Lucas Molas committed
Jan 3, 2022 at 12:00 UTC
6817fd474467face64b08440e5c938e6566c39d5
10 files changed
+88
-68
cmd/ipfs/daemon.go
+3
-2
@@ -298,7 +298,8 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
298
}
299
300
// Read Migration section of IPFS config
301
- migrationCfg, err := migrations.ReadMigrationConfig(cctx.ConfigRoot)
301
+ configFileOpt, _ := req.Options[commands.ConfigFileOption].(string)
302
+ migrationCfg, err := migrations.ReadMigrationConfig(cctx.ConfigRoot, configFileOpt)
303
if err != nil {
304
return err
305
}
@@ -309,7 +310,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
310
// to construct the particular IPFS fetcher implementation used here,
311
// which is called only if an IPFS fetcher is needed.
312
newIpfsFetcher := func(distPath string) migrations.Fetcher {
312
- return ipfsfetcher.NewIpfsFetcher(distPath, 0, &cctx.ConfigRoot)
313
+ return ipfsfetcher.NewIpfsFetcher(distPath, 0, &cctx.ConfigRoot, configFileOpt)
314
}
315
316
// Fetch migrations from current distribution, or location from environ
cmd/ipfs/main.go
+1
-1
@@ -303,7 +303,7 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
303
}
304
305
func getRepoPath(req *cmds.Request) (string, error) {
306
- repoOpt, found := req.Options["config"].(string)
306
+ repoOpt, found := req.Options[corecmds.RepoDirOption].(string)
307
if found && repoOpt != "" {
308
return repoOpt, nil
309
}
config/config.go
+17
-3
@@ -76,9 +76,23 @@ func Path(configroot, extension string) (string, error) {
76
}
77
78
// Filename returns the configuration file path given a configuration root
79
-// directory. If the configuration root directory is empty, use the default one
80
-func Filename(configroot string) (string, error) {
81
- return Path(configroot, DefaultConfigFile)
79
+// directory and a user-provided configuration file path argument with the
80
+// following rules:
81
+// * If the user-provided configuration file path is empty, use the default one.
82
+// * If the configuration root directory is empty, use the default one.
83
+// * If the user-provided configuration file path is only a file name, use the
84
+// configuration root directory, otherwise use only the user-provided path
85
+// and ignore the configuration root.
86
+func Filename(configroot string, userConfigFile string) (string, error) {
87
+ if userConfigFile == "" {
88
+ return Path(configroot, DefaultConfigFile)
89
+ }
90
+
91
+ if filepath.Dir(userConfigFile) == "." {
92
+ return Path(configroot, userConfigFile)
93
+ }
94
+
95
+ return userConfigFile, nil
96
}
97
98
// HumanOutput gets a config value ready for printing
core/commands/config.go
+4
-2
@@ -186,7 +186,8 @@ NOTE: For security reasons, this command will omit your private key and remote s
186
return err
187
}
188
189
- fname, err := config.Filename(cfgRoot)
189
+ configFileOpt, _ := req.Options[ConfigFileOption].(string)
190
+ fname, err := config.Filename(cfgRoot, configFileOpt)
191
if err != nil {
192
return err
193
}
@@ -291,7 +292,8 @@ variable set to your preferred text editor.
292
return err
293
}
294
294
- filename, err := config.Filename(cfgRoot)
295
+ configFileOpt, _ := req.Options[ConfigFileOption].(string)
296
+ filename, err := config.Filename(cfgRoot, configFileOpt)
297
if err != nil {
298
return err
299
}
core/commands/root.go
+5
-1
@@ -19,6 +19,8 @@ var log = logging.Logger("core/commands")
19
var ErrNotOnline = errors.New("this command must be run in online mode. Try running 'ipfs daemon' first")
20
21
const (
22
+ RepoDirOption = "repo-dir"
23
+ ConfigFileOption = "config-file"
24
ConfigOption = "config"
25
DebugOption = "debug"
26
LocalOption = "local" // DEPRECATED: use OfflineOption
@@ -94,7 +96,9 @@ The CLI will exit with one of the following values:
96
`,
97
},
98
Options: []cmds.Option{
97
- cmds.StringOption(ConfigOption, "c", "Path to the configuration file to use."),
99
+ cmds.StringOption(RepoDirOption, "Path to the repository directory to use."),
100
+ cmds.StringOption(ConfigFileOption, "Path to the configuration file to use."),
101
+ cmds.StringOption(ConfigOption, "c", "[DEPRECATED] Path to the configuration file to use."),
102
cmds.BoolOption(DebugOption, "D", "Operate in debug mode."),
103
cmds.BoolOption(cmds.OptLongHelp, "Show the full command help text."),
104
cmds.BoolOption(cmds.OptShortHelp, "Show a short version of the command help text."),
repo/fsrepo/fsrepo.go
+32
-35
@@ -96,6 +96,9 @@ type FSRepo struct {
96
closed bool
97
// path is the file-system path
98
path string
99
+ // Path to the configuration file that may or may not be inside the FSRepo
100
+ // path (see config.Filename for more details).
101
+ configFilePath string
102
// lockfile is the file system lock to prevent others from opening
103
// the same fsrepo path concurrently
104
lockfile io.Closer
@@ -111,16 +114,25 @@ var _ repo.Repo = (*FSRepo)(nil)
114
// initialized.
115
func Open(repoPath string) (repo.Repo, error) {
116
fn := func() (repo.Repo, error) {
114
- return open(repoPath)
117
+ return open(repoPath, "")
118
}
119
return onlyOne.Open(repoPath, fn)
120
}
121
119
-func open(repoPath string) (repo.Repo, error) {
122
+// OpenWithUserConfig is the equivalent to the Open function above but with the
123
+// option to set the configuration file path instead of using the default.
124
+func OpenWithUserConfig(repoPath string, userConfigFilePath string) (repo.Repo, error) {
125
+ fn := func() (repo.Repo, error) {
126
+ return open(repoPath, userConfigFilePath)
127
+ }
128
+ return onlyOne.Open(repoPath, fn)
129
+}
130
+
131
+func open(repoPath string, userConfigFilePath string) (repo.Repo, error) {
132
packageLock.Lock()
133
defer packageLock.Unlock()
134
123
- r, err := newFSRepo(repoPath)
135
+ r, err := newFSRepo(repoPath, userConfigFilePath)
136
if err != nil {
137
return nil, err
138
}
@@ -185,13 +197,19 @@ func open(repoPath string) (repo.Repo, error) {
197
return r, nil
198
}
199
188
-func newFSRepo(rpath string) (*FSRepo, error) {
200
+func newFSRepo(rpath string, userConfigFilePath string) (*FSRepo, error) {
201
expPath, err := homedir.Expand(filepath.Clean(rpath))
202
if err != nil {
203
return nil, err
204
}
205
194
- return &FSRepo{path: expPath}, nil
206
+ configFilePath, err := config.Filename(rpath, userConfigFilePath)
207
+ if err != nil {
208
+ // FIXME: Personalize this when the user config path is "".
209
+ return nil, fmt.Errorf("finding config filepath from repo %s and user config %s: %w",
210
+ rpath, userConfigFilePath, err)
211
+ }
212
+ return &FSRepo{path: expPath, configFilePath: configFilePath}, nil
213
}
214
215
func checkInitialized(path string) error {
@@ -208,7 +226,7 @@ func checkInitialized(path string) error {
226
// configIsInitialized returns true if the repo is initialized at
227
// provided |path|.
228
func configIsInitialized(path string) bool {
211
- configFilename, err := config.Filename(path)
229
+ configFilename, err := config.Filename(path, "")
230
if err != nil {
231
return false
232
}
@@ -222,7 +240,7 @@ func initConfig(path string, conf *config.Config) error {
240
if configIsInitialized(path) {
241
return nil
242
}
225
- configFilename, err := config.Filename(path)
243
+ configFilename, err := config.Filename(path, "")
244
if err != nil {
245
return err
246
}
@@ -372,11 +390,7 @@ func (r *FSRepo) SetAPIAddr(addr ma.Multiaddr) error {
390
391
// openConfig returns an error if the config file is not present.
392
func (r *FSRepo) openConfig() error {
375
- configFilename, err := config.Filename(r.path)
376
- if err != nil {
377
- return err
378
- }
379
- conf, err := serialize.Load(configFilename)
393
+ conf, err := serialize.Load(r.configFilePath)
394
if err != nil {
395
return err
396
}
@@ -507,12 +521,7 @@ func (r *FSRepo) BackupConfig(prefix string) (string, error) {
521
}
522
defer temp.Close()
523
510
- configFilename, err := config.Filename(r.path)
511
- if err != nil {
512
- return "", err
513
- }
514
-
515
- orig, err := os.OpenFile(configFilename, os.O_RDONLY, 0600)
524
+ orig, err := os.OpenFile(r.configFilePath, os.O_RDONLY, 0600)
525
if err != nil {
526
return "", err
527
}
@@ -546,15 +555,11 @@ func (r *FSRepo) SetConfig(updated *config.Config) error {
555
packageLock.Lock()
556
defer packageLock.Unlock()
557
549
- configFilename, err := config.Filename(r.path)
550
- if err != nil {
551
- return err
552
- }
558
// to avoid clobbering user-provided keys, must read the config from disk
559
// as a map, write the updated struct values to the map and write the map
560
// to disk.
561
var mapconf map[string]interface{}
557
- if err := serialize.ReadConfigFile(configFilename, &mapconf); err != nil {
562
+ if err := serialize.ReadConfigFile(r.configFilePath, &mapconf); err != nil {
563
return err
564
}
565
m, err := config.ToMap(updated)
@@ -562,7 +567,7 @@ func (r *FSRepo) SetConfig(updated *config.Config) error {
567
return err
568
}
569
mergedMap := common.MapMergeDeep(mapconf, m)
565
- if err := serialize.WriteConfigFile(configFilename, mergedMap); err != nil {
570
+ if err := serialize.WriteConfigFile(r.configFilePath, mergedMap); err != nil {
571
return err
572
}
573
// Do not use `*r.config = ...`. This will modify the *shared* config
@@ -580,12 +585,8 @@ func (r *FSRepo) GetConfigKey(key string) (interface{}, error) {
585
return nil, errors.New("repo is closed")
586
}
587
583
- filename, err := config.Filename(r.path)
584
- if err != nil {
585
- return nil, err
586
- }
588
var cfg map[string]interface{}
588
- if err := serialize.ReadConfigFile(filename, &cfg); err != nil {
589
+ if err := serialize.ReadConfigFile(r.configFilePath, &cfg); err != nil {
590
return nil, err
591
}
592
return common.MapGetKV(cfg, key)
@@ -600,13 +601,9 @@ func (r *FSRepo) SetConfigKey(key string, value interface{}) error {
601
return errors.New("repo is closed")
602
}
603
603
- filename, err := config.Filename(r.path)
604
- if err != nil {
605
- return err
606
- }
604
// Load into a map so we don't end up writing any additional defaults to the config file.
605
var mapconf map[string]interface{}
609
- if err := serialize.ReadConfigFile(filename, &mapconf); err != nil {
606
+ if err := serialize.ReadConfigFile(r.configFilePath, &mapconf); err != nil {
607
return err
608
}
609
@@ -636,7 +633,7 @@ func (r *FSRepo) SetConfigKey(key string, value interface{}) error {
633
}
634
r.config = conf
635
639
- if err := serialize.WriteConfigFile(filename, mapconf); err != nil {
636
+ if err := serialize.WriteConfigFile(r.configFilePath, mapconf); err != nil {
637
return err
638
}
639
repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go
+12
-10
@@ -33,9 +33,10 @@ const (
33
)
34
35
type IpfsFetcher struct {
36
- distPath string
37
- limit int64
38
- repoRoot *string
36
+ distPath string
37
+ limit int64
38
+ repoRoot *string
39
+ userConfigFile string
40
41
openOnce sync.Once
42
openErr error
@@ -62,11 +63,12 @@ var _ migrations.Fetcher = (*IpfsFetcher)(nil)
63
// Bootstrap and peer information in read from the IPFS config file in
64
// repoRoot, unless repoRoot is nil. If repoRoot is empty (""), then read the
65
// config from the default IPFS directory.
65
-func NewIpfsFetcher(distPath string, fetchLimit int64, repoRoot *string) *IpfsFetcher {
66
+func NewIpfsFetcher(distPath string, fetchLimit int64, repoRoot *string, userConfigFile string) *IpfsFetcher {
67
f := &IpfsFetcher{
67
- limit: defaultFetchLimit,
68
- distPath: migrations.LatestIpfsDist,
69
- repoRoot: repoRoot,
68
+ limit: defaultFetchLimit,
69
+ distPath: migrations.LatestIpfsDist,
70
+ repoRoot: repoRoot,
71
+ userConfigFile: userConfigFile,
72
}
73
74
if distPath != "" {
@@ -92,7 +94,7 @@ func (f *IpfsFetcher) Fetch(ctx context.Context, filePath string) ([]byte, error
94
// Initialize and start IPFS node on first call to Fetch, since the fetcher
95
// may be created by not used.
96
f.openOnce.Do(func() {
95
- bootstrap, peers := readIpfsConfig(f.repoRoot)
97
+ bootstrap, peers := readIpfsConfig(f.repoRoot, f.userConfigFile)
98
f.ipfsTmpDir, f.openErr = initTempNode(ctx, bootstrap, peers)
99
if f.openErr != nil {
100
return
@@ -288,12 +290,12 @@ func parsePath(fetchPath string) (ipath.Path, error) {
290
return ipfsPath, ipfsPath.IsValid()
291
}
292
291
-func readIpfsConfig(repoRoot *string) (bootstrap []string, peers []peer.AddrInfo) {
293
+func readIpfsConfig(repoRoot *string, userConfigFile string) (bootstrap []string, peers []peer.AddrInfo) {
294
if repoRoot == nil {
295
return
296
}
297
296
- cfgPath, err := config.Filename(*repoRoot)
298
+ cfgPath, err := config.Filename(*repoRoot, userConfigFile)
299
if err != nil {
300
fmt.Fprintln(os.Stderr, err)
301
return
repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher_test.go
+7
-7
@@ -26,7 +26,7 @@ func TestIpfsFetcher(t *testing.T) {
26
ctx, cancel := context.WithCancel(context.Background())
27
defer cancel()
28
29
- fetcher := NewIpfsFetcher("", 0, nil)
29
+ fetcher := NewIpfsFetcher("", 0, nil, "")
30
defer fetcher.Close()
31
32
out, err := fetcher.Fetch(ctx, "go-ipfs/versions")
@@ -62,7 +62,7 @@ func TestInitIpfsFetcher(t *testing.T) {
62
ctx, cancel := context.WithCancel(context.Background())
63
defer cancel()
64
65
- f := NewIpfsFetcher("", 0, nil)
65
+ f := NewIpfsFetcher("", 0, nil, "")
66
defer f.Close()
67
68
// Init ipfs repo
@@ -132,7 +132,7 @@ func TestReadIpfsConfig(t *testing.T) {
132
`
133
134
noSuchDir := "no_such_dir-5953aa51-1145-4efd-afd1-a069075fcf76"
135
- bootstrap, peers := readIpfsConfig(&noSuchDir)
135
+ bootstrap, peers := readIpfsConfig(&noSuchDir, "")
136
if bootstrap != nil {
137
t.Error("expected nil bootstrap")
138
}
@@ -142,12 +142,12 @@ func TestReadIpfsConfig(t *testing.T) {
142
143
tmpDir := makeConfig(t, testConfig)
144
145
- bootstrap, peers = readIpfsConfig(nil)
145
+ bootstrap, peers = readIpfsConfig(nil, "")
146
if bootstrap != nil || peers != nil {
147
t.Fatal("expected nil ipfs config items")
148
}
149
150
- bootstrap, peers = readIpfsConfig(&tmpDir)
150
+ bootstrap, peers = readIpfsConfig(&tmpDir, "")
151
if len(bootstrap) != 2 {
152
t.Fatal("wrong number of bootstrap addresses")
153
}
@@ -189,7 +189,7 @@ func TestBadBootstrappingIpfsConfig(t *testing.T) {
189
190
tmpDir := makeConfig(t, configBadBootstrap)
191
192
- bootstrap, peers := readIpfsConfig(&tmpDir)
192
+ bootstrap, peers := readIpfsConfig(&tmpDir, "")
193
if bootstrap != nil {
194
t.Fatal("expected nil bootstrap")
195
}
@@ -219,7 +219,7 @@ func TestBadPeersIpfsConfig(t *testing.T) {
219
220
tmpDir := makeConfig(t, configBadPeers)
221
222
- bootstrap, peers := readIpfsConfig(&tmpDir)
222
+ bootstrap, peers := readIpfsConfig(&tmpDir, "")
223
if peers != nil {
224
t.Fatal("expected nil peers")
225
}
repo/fsrepo/migrations/migrations.go
+2
-2
@@ -115,12 +115,12 @@ func ExeName(name string) string {
115
// ReadMigrationConfig reads the Migration section of the IPFS config, avoiding
116
// reading anything other than the Migration section. That way, we're free to
117
// make arbitrary changes to all _other_ sections in migrations.
118
-func ReadMigrationConfig(repoRoot string) (*config.Migration, error) {
118
+func ReadMigrationConfig(repoRoot string, userConfigFile string) (*config.Migration, error) {
119
var cfg struct {
120
Migration config.Migration
121
}
122
123
- cfgPath, err := config.Filename(repoRoot)
123
+ cfgPath, err := config.Filename(repoRoot, userConfigFile)
124
if err != nil {
125
return nil, err
126
}
repo/fsrepo/migrations/migrations_test.go
+5
-5
@@ -221,7 +221,7 @@ var testConfig = `
221
func TestReadMigrationConfigDefaults(t *testing.T) {
222
tmpDir := makeConfig(t, "{}")
223
224
- cfg, err := ReadMigrationConfig(tmpDir)
224
+ cfg, err := ReadMigrationConfig(tmpDir, "")
225
if err != nil {
226
t.Fatal(err)
227
}
@@ -243,7 +243,7 @@ func TestReadMigrationConfigDefaults(t *testing.T) {
243
func TestReadMigrationConfigErrors(t *testing.T) {
244
tmpDir := makeConfig(t, `{"Migration": {"Keep": "badvalue"}}`)
245
246
- _, err := ReadMigrationConfig(tmpDir)
246
+ _, err := ReadMigrationConfig(tmpDir, "")
247
if err == nil {
248
t.Fatal("expected error")
249
}
@@ -252,13 +252,13 @@ func TestReadMigrationConfigErrors(t *testing.T) {
252
}
253
254
os.RemoveAll(tmpDir)
255
- _, err = ReadMigrationConfig(tmpDir)
255
+ _, err = ReadMigrationConfig(tmpDir, "")
256
if err == nil {
257
t.Fatal("expected error")
258
}
259
260
tmpDir = makeConfig(t, `}{`)
261
- _, err = ReadMigrationConfig(tmpDir)
261
+ _, err = ReadMigrationConfig(tmpDir, "")
262
if err == nil {
263
t.Fatal("expected error")
264
}
@@ -267,7 +267,7 @@ func TestReadMigrationConfigErrors(t *testing.T) {
267
func TestReadMigrationConfig(t *testing.T) {
268
tmpDir := makeConfig(t, testConfig)
269
270
- cfg, err := ReadMigrationConfig(tmpDir)
270
+ cfg, err := ReadMigrationConfig(tmpDir, "")
271
if err != nil {
272
t.Fatal(err)
273
}