Skip to content

Commit d6077b8

Browse files
authored
Merge pull request #8634 from ipfs/schomatis/feat/cmds/use-custom-config-file-path
feat(cmds): allow to set the configuration file path
2 parents 7162a63 + e172ad2 commit d6077b8

File tree

12 files changed

+148
-121
lines changed

12 files changed

+148
-121
lines changed

cmd/ipfs/daemon.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,8 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
298298
}
299299

300300
// 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)
302303
if err != nil {
303304
return err
304305
}
@@ -309,7 +310,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
309310
// to construct the particular IPFS fetcher implementation used here,
310311
// which is called only if an IPFS fetcher is needed.
311312
newIpfsFetcher := func(distPath string) migrations.Fetcher {
312-
return ipfsfetcher.NewIpfsFetcher(distPath, 0, &cctx.ConfigRoot)
313+
return ipfsfetcher.NewIpfsFetcher(distPath, 0, &cctx.ConfigRoot, configFileOpt)
313314
}
314315

315316
// Fetch migrations from current distribution, or location from environ

cmd/ipfs/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
303303
}
304304

305305
func getRepoPath(req *cmds.Request) (string, error) {
306-
repoOpt, found := req.Options["config"].(string)
306+
repoOpt, found := req.Options[corecmds.RepoDirOption].(string)
307307
if found && repoOpt != "" {
308308
return repoOpt, nil
309309
}

config/config.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,23 @@ func Path(configroot, extension string) (string, error) {
7676
}
7777

7878
// 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
8296
}
8397

8498
// HumanOutput gets a config value ready for printing

core/commands/config.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ NOTE: For security reasons, this command will omit your private key and remote s
186186
return err
187187
}
188188

189-
fname, err := config.Filename(cfgRoot)
189+
configFileOpt, _ := req.Options[ConfigFileOption].(string)
190+
fname, err := config.Filename(cfgRoot, configFileOpt)
190191
if err != nil {
191192
return err
192193
}
@@ -291,7 +292,8 @@ variable set to your preferred text editor.
291292
return err
292293
}
293294

294-
filename, err := config.Filename(cfgRoot)
295+
configFileOpt, _ := req.Options[ConfigFileOption].(string)
296+
filename, err := config.Filename(cfgRoot, configFileOpt)
295297
if err != nil {
296298
return err
297299
}

core/commands/root.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ var log = logging.Logger("core/commands")
1919
var ErrNotOnline = errors.New("this command must be run in online mode. Try running 'ipfs daemon' first")
2020

2121
const (
22+
RepoDirOption = "repo-dir"
23+
ConfigFileOption = "config-file"
2224
ConfigOption = "config"
2325
DebugOption = "debug"
2426
LocalOption = "local" // DEPRECATED: use OfflineOption
@@ -94,7 +96,9 @@ The CLI will exit with one of the following values:
9496
`,
9597
},
9698
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."),
98102
cmds.BoolOption(DebugOption, "D", "Operate in debug mode."),
99103
cmds.BoolOption(cmds.OptLongHelp, "Show the full command help text."),
100104
cmds.BoolOption(cmds.OptShortHelp, "Show a short version of the command help text."),

repo/fsrepo/fsrepo.go

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ type FSRepo struct {
9696
closed bool
9797
// path is the file-system path
9898
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
99102
// lockfile is the file system lock to prevent others from opening
100103
// the same fsrepo path concurrently
101104
lockfile io.Closer
@@ -111,16 +114,25 @@ var _ repo.Repo = (*FSRepo)(nil)
111114
// initialized.
112115
func Open(repoPath string) (repo.Repo, error) {
113116
fn := func() (repo.Repo, error) {
114-
return open(repoPath)
117+
return open(repoPath, "")
115118
}
116119
return onlyOne.Open(repoPath, fn)
117120
}
118121

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) {
120132
packageLock.Lock()
121133
defer packageLock.Unlock()
122134

123-
r, err := newFSRepo(repoPath)
135+
r, err := newFSRepo(repoPath, userConfigFilePath)
124136
if err != nil {
125137
return nil, err
126138
}
@@ -185,13 +197,19 @@ func open(repoPath string) (repo.Repo, error) {
185197
return r, nil
186198
}
187199

188-
func newFSRepo(rpath string) (*FSRepo, error) {
200+
func newFSRepo(rpath string, userConfigFilePath string) (*FSRepo, error) {
189201
expPath, err := homedir.Expand(filepath.Clean(rpath))
190202
if err != nil {
191203
return nil, err
192204
}
193205

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
195213
}
196214

197215
func checkInitialized(path string) error {
@@ -208,7 +226,7 @@ func checkInitialized(path string) error {
208226
// configIsInitialized returns true if the repo is initialized at
209227
// provided |path|.
210228
func configIsInitialized(path string) bool {
211-
configFilename, err := config.Filename(path)
229+
configFilename, err := config.Filename(path, "")
212230
if err != nil {
213231
return false
214232
}
@@ -222,7 +240,7 @@ func initConfig(path string, conf *config.Config) error {
222240
if configIsInitialized(path) {
223241
return nil
224242
}
225-
configFilename, err := config.Filename(path)
243+
configFilename, err := config.Filename(path, "")
226244
if err != nil {
227245
return err
228246
}
@@ -372,11 +390,7 @@ func (r *FSRepo) SetAPIAddr(addr ma.Multiaddr) error {
372390

373391
// openConfig returns an error if the config file is not present.
374392
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)
380394
if err != nil {
381395
return err
382396
}
@@ -507,12 +521,7 @@ func (r *FSRepo) BackupConfig(prefix string) (string, error) {
507521
}
508522
defer temp.Close()
509523

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)
516525
if err != nil {
517526
return "", err
518527
}
@@ -546,23 +555,19 @@ func (r *FSRepo) SetConfig(updated *config.Config) error {
546555
packageLock.Lock()
547556
defer packageLock.Unlock()
548557

549-
configFilename, err := config.Filename(r.path)
550-
if err != nil {
551-
return err
552-
}
553558
// to avoid clobbering user-provided keys, must read the config from disk
554559
// as a map, write the updated struct values to the map and write the map
555560
// to disk.
556561
var mapconf map[string]interface{}
557-
if err := serialize.ReadConfigFile(configFilename, &mapconf); err != nil {
562+
if err := serialize.ReadConfigFile(r.configFilePath, &mapconf); err != nil {
558563
return err
559564
}
560565
m, err := config.ToMap(updated)
561566
if err != nil {
562567
return err
563568
}
564569
mergedMap := common.MapMergeDeep(mapconf, m)
565-
if err := serialize.WriteConfigFile(configFilename, mergedMap); err != nil {
570+
if err := serialize.WriteConfigFile(r.configFilePath, mergedMap); err != nil {
566571
return err
567572
}
568573
// Do not use `*r.config = ...`. This will modify the *shared* config
@@ -580,12 +585,8 @@ func (r *FSRepo) GetConfigKey(key string) (interface{}, error) {
580585
return nil, errors.New("repo is closed")
581586
}
582587

583-
filename, err := config.Filename(r.path)
584-
if err != nil {
585-
return nil, err
586-
}
587588
var cfg map[string]interface{}
588-
if err := serialize.ReadConfigFile(filename, &cfg); err != nil {
589+
if err := serialize.ReadConfigFile(r.configFilePath, &cfg); err != nil {
589590
return nil, err
590591
}
591592
return common.MapGetKV(cfg, key)
@@ -600,13 +601,9 @@ func (r *FSRepo) SetConfigKey(key string, value interface{}) error {
600601
return errors.New("repo is closed")
601602
}
602603

603-
filename, err := config.Filename(r.path)
604-
if err != nil {
605-
return err
606-
}
607604
// Load into a map so we don't end up writing any additional defaults to the config file.
608605
var mapconf map[string]interface{}
609-
if err := serialize.ReadConfigFile(filename, &mapconf); err != nil {
606+
if err := serialize.ReadConfigFile(r.configFilePath, &mapconf); err != nil {
610607
return err
611608
}
612609

@@ -636,7 +633,7 @@ func (r *FSRepo) SetConfigKey(key string, value interface{}) error {
636633
}
637634
r.config = conf
638635

639-
if err := serialize.WriteConfigFile(filename, mapconf); err != nil {
636+
if err := serialize.WriteConfigFile(r.configFilePath, mapconf); err != nil {
640637
return err
641638
}
642639

repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ const (
3333
)
3434

3535
type IpfsFetcher struct {
36-
distPath string
37-
limit int64
38-
repoRoot *string
36+
distPath string
37+
limit int64
38+
repoRoot *string
39+
userConfigFile string
3940

4041
openOnce sync.Once
4142
openErr error
@@ -62,11 +63,12 @@ var _ migrations.Fetcher = (*IpfsFetcher)(nil)
6263
// Bootstrap and peer information in read from the IPFS config file in
6364
// repoRoot, unless repoRoot is nil. If repoRoot is empty (""), then read the
6465
// 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 {
6667
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,
7072
}
7173

7274
if distPath != "" {
@@ -92,7 +94,7 @@ func (f *IpfsFetcher) Fetch(ctx context.Context, filePath string) ([]byte, error
9294
// Initialize and start IPFS node on first call to Fetch, since the fetcher
9395
// may be created by not used.
9496
f.openOnce.Do(func() {
95-
bootstrap, peers := readIpfsConfig(f.repoRoot)
97+
bootstrap, peers := readIpfsConfig(f.repoRoot, f.userConfigFile)
9698
f.ipfsTmpDir, f.openErr = initTempNode(ctx, bootstrap, peers)
9799
if f.openErr != nil {
98100
return
@@ -288,12 +290,12 @@ func parsePath(fetchPath string) (ipath.Path, error) {
288290
return ipfsPath, ipfsPath.IsValid()
289291
}
290292

291-
func readIpfsConfig(repoRoot *string) (bootstrap []string, peers []peer.AddrInfo) {
293+
func readIpfsConfig(repoRoot *string, userConfigFile string) (bootstrap []string, peers []peer.AddrInfo) {
292294
if repoRoot == nil {
293295
return
294296
}
295297

296-
cfgPath, err := config.Filename(*repoRoot)
298+
cfgPath, err := config.Filename(*repoRoot, userConfigFile)
297299
if err != nil {
298300
fmt.Fprintln(os.Stderr, err)
299301
return

repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func TestIpfsFetcher(t *testing.T) {
2626
ctx, cancel := context.WithCancel(context.Background())
2727
defer cancel()
2828

29-
fetcher := NewIpfsFetcher("", 0, nil)
29+
fetcher := NewIpfsFetcher("", 0, nil, "")
3030
defer fetcher.Close()
3131

3232
out, err := fetcher.Fetch(ctx, "go-ipfs/versions")
@@ -62,7 +62,7 @@ func TestInitIpfsFetcher(t *testing.T) {
6262
ctx, cancel := context.WithCancel(context.Background())
6363
defer cancel()
6464

65-
f := NewIpfsFetcher("", 0, nil)
65+
f := NewIpfsFetcher("", 0, nil, "")
6666
defer f.Close()
6767

6868
// Init ipfs repo
@@ -132,7 +132,7 @@ func TestReadIpfsConfig(t *testing.T) {
132132
`
133133

134134
noSuchDir := "no_such_dir-5953aa51-1145-4efd-afd1-a069075fcf76"
135-
bootstrap, peers := readIpfsConfig(&noSuchDir)
135+
bootstrap, peers := readIpfsConfig(&noSuchDir, "")
136136
if bootstrap != nil {
137137
t.Error("expected nil bootstrap")
138138
}
@@ -142,12 +142,12 @@ func TestReadIpfsConfig(t *testing.T) {
142142

143143
tmpDir := makeConfig(t, testConfig)
144144

145-
bootstrap, peers = readIpfsConfig(nil)
145+
bootstrap, peers = readIpfsConfig(nil, "")
146146
if bootstrap != nil || peers != nil {
147147
t.Fatal("expected nil ipfs config items")
148148
}
149149

150-
bootstrap, peers = readIpfsConfig(&tmpDir)
150+
bootstrap, peers = readIpfsConfig(&tmpDir, "")
151151
if len(bootstrap) != 2 {
152152
t.Fatal("wrong number of bootstrap addresses")
153153
}
@@ -189,7 +189,7 @@ func TestBadBootstrappingIpfsConfig(t *testing.T) {
189189

190190
tmpDir := makeConfig(t, configBadBootstrap)
191191

192-
bootstrap, peers := readIpfsConfig(&tmpDir)
192+
bootstrap, peers := readIpfsConfig(&tmpDir, "")
193193
if bootstrap != nil {
194194
t.Fatal("expected nil bootstrap")
195195
}
@@ -219,7 +219,7 @@ func TestBadPeersIpfsConfig(t *testing.T) {
219219

220220
tmpDir := makeConfig(t, configBadPeers)
221221

222-
bootstrap, peers := readIpfsConfig(&tmpDir)
222+
bootstrap, peers := readIpfsConfig(&tmpDir, "")
223223
if peers != nil {
224224
t.Fatal("expected nil peers")
225225
}

repo/fsrepo/migrations/migrations.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,12 @@ func ExeName(name string) string {
115115
// ReadMigrationConfig reads the Migration section of the IPFS config, avoiding
116116
// reading anything other than the Migration section. That way, we're free to
117117
// 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) {
119119
var cfg struct {
120120
Migration config.Migration
121121
}
122122

123-
cfgPath, err := config.Filename(repoRoot)
123+
cfgPath, err := config.Filename(repoRoot, userConfigFile)
124124
if err != nil {
125125
return nil, err
126126
}

0 commit comments

Comments
 (0)