mirror of
https://github.com/ipfs/kubo.git
synced 2025-05-17 15:06:47 +08:00
feat(cmds): allow to set the configuration file path
This commit is contained in:
@ -298,7 +298,8 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
|
||||
}
|
||||
|
||||
// Read Migration section of IPFS config
|
||||
migrationCfg, err := migrations.ReadMigrationConfig(cctx.ConfigRoot)
|
||||
configFileOpt, _ := req.Options[commands.ConfigFileOption].(string)
|
||||
migrationCfg, err := migrations.ReadMigrationConfig(cctx.ConfigRoot, configFileOpt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -309,7 +310,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
|
||||
// to construct the particular IPFS fetcher implementation used here,
|
||||
// which is called only if an IPFS fetcher is needed.
|
||||
newIpfsFetcher := func(distPath string) migrations.Fetcher {
|
||||
return ipfsfetcher.NewIpfsFetcher(distPath, 0, &cctx.ConfigRoot)
|
||||
return ipfsfetcher.NewIpfsFetcher(distPath, 0, &cctx.ConfigRoot, configFileOpt)
|
||||
}
|
||||
|
||||
// Fetch migrations from current distribution, or location from environ
|
||||
|
@ -303,7 +303,7 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
|
||||
}
|
||||
|
||||
func getRepoPath(req *cmds.Request) (string, error) {
|
||||
repoOpt, found := req.Options["config"].(string)
|
||||
repoOpt, found := req.Options[corecmds.RepoDirOption].(string)
|
||||
if found && repoOpt != "" {
|
||||
return repoOpt, nil
|
||||
}
|
||||
|
@ -76,9 +76,23 @@ func Path(configroot, extension string) (string, error) {
|
||||
}
|
||||
|
||||
// Filename returns the configuration file path given a configuration root
|
||||
// directory. If the configuration root directory is empty, use the default one
|
||||
func Filename(configroot string) (string, error) {
|
||||
return Path(configroot, DefaultConfigFile)
|
||||
// directory and a user-provided configuration file path argument with the
|
||||
// following rules:
|
||||
// * If the user-provided configuration file path is empty, use the default one.
|
||||
// * If the configuration root directory is empty, use the default one.
|
||||
// * If the user-provided configuration file path is only a file name, use the
|
||||
// configuration root directory, otherwise use only the user-provided path
|
||||
// and ignore the configuration root.
|
||||
func Filename(configroot string, userConfigFile string) (string, error) {
|
||||
if userConfigFile == "" {
|
||||
return Path(configroot, DefaultConfigFile)
|
||||
}
|
||||
|
||||
if filepath.Dir(userConfigFile) == "." {
|
||||
return Path(configroot, userConfigFile)
|
||||
}
|
||||
|
||||
return userConfigFile, nil
|
||||
}
|
||||
|
||||
// HumanOutput gets a config value ready for printing
|
||||
|
@ -186,7 +186,8 @@ NOTE: For security reasons, this command will omit your private key and remote s
|
||||
return err
|
||||
}
|
||||
|
||||
fname, err := config.Filename(cfgRoot)
|
||||
configFileOpt, _ := req.Options[ConfigFileOption].(string)
|
||||
fname, err := config.Filename(cfgRoot, configFileOpt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -291,7 +292,8 @@ variable set to your preferred text editor.
|
||||
return err
|
||||
}
|
||||
|
||||
filename, err := config.Filename(cfgRoot)
|
||||
configFileOpt, _ := req.Options[ConfigFileOption].(string)
|
||||
filename, err := config.Filename(cfgRoot, configFileOpt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ var log = logging.Logger("core/commands")
|
||||
var ErrNotOnline = errors.New("this command must be run in online mode. Try running 'ipfs daemon' first")
|
||||
|
||||
const (
|
||||
RepoDirOption = "repo-dir"
|
||||
ConfigFileOption = "config-file"
|
||||
ConfigOption = "config"
|
||||
DebugOption = "debug"
|
||||
LocalOption = "local" // DEPRECATED: use OfflineOption
|
||||
@ -94,7 +96,9 @@ The CLI will exit with one of the following values:
|
||||
`,
|
||||
},
|
||||
Options: []cmds.Option{
|
||||
cmds.StringOption(ConfigOption, "c", "Path to the configuration file to use."),
|
||||
cmds.StringOption(RepoDirOption, "Path to the repository directory to use."),
|
||||
cmds.StringOption(ConfigFileOption, "Path to the configuration file to use."),
|
||||
cmds.StringOption(ConfigOption, "c", "[DEPRECATED] Path to the configuration file to use."),
|
||||
cmds.BoolOption(DebugOption, "D", "Operate in debug mode."),
|
||||
cmds.BoolOption(cmds.OptLongHelp, "Show the full command help text."),
|
||||
cmds.BoolOption(cmds.OptShortHelp, "Show a short version of the command help text."),
|
||||
|
@ -96,6 +96,9 @@ type FSRepo struct {
|
||||
closed bool
|
||||
// path is the file-system path
|
||||
path string
|
||||
// Path to the configuration file that may or may not be inside the FSRepo
|
||||
// path (see config.Filename for more details).
|
||||
configFilePath string
|
||||
// lockfile is the file system lock to prevent others from opening
|
||||
// the same fsrepo path concurrently
|
||||
lockfile io.Closer
|
||||
@ -111,16 +114,25 @@ var _ repo.Repo = (*FSRepo)(nil)
|
||||
// initialized.
|
||||
func Open(repoPath string) (repo.Repo, error) {
|
||||
fn := func() (repo.Repo, error) {
|
||||
return open(repoPath)
|
||||
return open(repoPath, "")
|
||||
}
|
||||
return onlyOne.Open(repoPath, fn)
|
||||
}
|
||||
|
||||
func open(repoPath string) (repo.Repo, error) {
|
||||
// OpenWithUserConfig is the equivalent to the Open function above but with the
|
||||
// option to set the configuration file path instead of using the default.
|
||||
func OpenWithUserConfig(repoPath string, userConfigFilePath string) (repo.Repo, error) {
|
||||
fn := func() (repo.Repo, error) {
|
||||
return open(repoPath, userConfigFilePath)
|
||||
}
|
||||
return onlyOne.Open(repoPath, fn)
|
||||
}
|
||||
|
||||
func open(repoPath string, userConfigFilePath string) (repo.Repo, error) {
|
||||
packageLock.Lock()
|
||||
defer packageLock.Unlock()
|
||||
|
||||
r, err := newFSRepo(repoPath)
|
||||
r, err := newFSRepo(repoPath, userConfigFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -185,13 +197,19 @@ func open(repoPath string) (repo.Repo, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func newFSRepo(rpath string) (*FSRepo, error) {
|
||||
func newFSRepo(rpath string, userConfigFilePath string) (*FSRepo, error) {
|
||||
expPath, err := homedir.Expand(filepath.Clean(rpath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &FSRepo{path: expPath}, nil
|
||||
configFilePath, err := config.Filename(rpath, userConfigFilePath)
|
||||
if err != nil {
|
||||
// FIXME: Personalize this when the user config path is "".
|
||||
return nil, fmt.Errorf("finding config filepath from repo %s and user config %s: %w",
|
||||
rpath, userConfigFilePath, err)
|
||||
}
|
||||
return &FSRepo{path: expPath, configFilePath: configFilePath}, nil
|
||||
}
|
||||
|
||||
func checkInitialized(path string) error {
|
||||
@ -208,7 +226,7 @@ func checkInitialized(path string) error {
|
||||
// configIsInitialized returns true if the repo is initialized at
|
||||
// provided |path|.
|
||||
func configIsInitialized(path string) bool {
|
||||
configFilename, err := config.Filename(path)
|
||||
configFilename, err := config.Filename(path, "")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@ -222,7 +240,7 @@ func initConfig(path string, conf *config.Config) error {
|
||||
if configIsInitialized(path) {
|
||||
return nil
|
||||
}
|
||||
configFilename, err := config.Filename(path)
|
||||
configFilename, err := config.Filename(path, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -372,11 +390,7 @@ func (r *FSRepo) SetAPIAddr(addr ma.Multiaddr) error {
|
||||
|
||||
// openConfig returns an error if the config file is not present.
|
||||
func (r *FSRepo) openConfig() error {
|
||||
configFilename, err := config.Filename(r.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conf, err := serialize.Load(configFilename)
|
||||
conf, err := serialize.Load(r.configFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -507,12 +521,7 @@ func (r *FSRepo) BackupConfig(prefix string) (string, error) {
|
||||
}
|
||||
defer temp.Close()
|
||||
|
||||
configFilename, err := config.Filename(r.path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
orig, err := os.OpenFile(configFilename, os.O_RDONLY, 0600)
|
||||
orig, err := os.OpenFile(r.configFilePath, os.O_RDONLY, 0600)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -546,15 +555,11 @@ func (r *FSRepo) SetConfig(updated *config.Config) error {
|
||||
packageLock.Lock()
|
||||
defer packageLock.Unlock()
|
||||
|
||||
configFilename, err := config.Filename(r.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// to avoid clobbering user-provided keys, must read the config from disk
|
||||
// as a map, write the updated struct values to the map and write the map
|
||||
// to disk.
|
||||
var mapconf map[string]interface{}
|
||||
if err := serialize.ReadConfigFile(configFilename, &mapconf); err != nil {
|
||||
if err := serialize.ReadConfigFile(r.configFilePath, &mapconf); err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := config.ToMap(updated)
|
||||
@ -562,7 +567,7 @@ func (r *FSRepo) SetConfig(updated *config.Config) error {
|
||||
return err
|
||||
}
|
||||
mergedMap := common.MapMergeDeep(mapconf, m)
|
||||
if err := serialize.WriteConfigFile(configFilename, mergedMap); err != nil {
|
||||
if err := serialize.WriteConfigFile(r.configFilePath, mergedMap); err != nil {
|
||||
return err
|
||||
}
|
||||
// Do not use `*r.config = ...`. This will modify the *shared* config
|
||||
@ -580,12 +585,8 @@ func (r *FSRepo) GetConfigKey(key string) (interface{}, error) {
|
||||
return nil, errors.New("repo is closed")
|
||||
}
|
||||
|
||||
filename, err := config.Filename(r.path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cfg map[string]interface{}
|
||||
if err := serialize.ReadConfigFile(filename, &cfg); err != nil {
|
||||
if err := serialize.ReadConfigFile(r.configFilePath, &cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return common.MapGetKV(cfg, key)
|
||||
@ -600,13 +601,9 @@ func (r *FSRepo) SetConfigKey(key string, value interface{}) error {
|
||||
return errors.New("repo is closed")
|
||||
}
|
||||
|
||||
filename, err := config.Filename(r.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Load into a map so we don't end up writing any additional defaults to the config file.
|
||||
var mapconf map[string]interface{}
|
||||
if err := serialize.ReadConfigFile(filename, &mapconf); err != nil {
|
||||
if err := serialize.ReadConfigFile(r.configFilePath, &mapconf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -636,7 +633,7 @@ func (r *FSRepo) SetConfigKey(key string, value interface{}) error {
|
||||
}
|
||||
r.config = conf
|
||||
|
||||
if err := serialize.WriteConfigFile(filename, mapconf); err != nil {
|
||||
if err := serialize.WriteConfigFile(r.configFilePath, mapconf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -33,9 +33,10 @@ const (
|
||||
)
|
||||
|
||||
type IpfsFetcher struct {
|
||||
distPath string
|
||||
limit int64
|
||||
repoRoot *string
|
||||
distPath string
|
||||
limit int64
|
||||
repoRoot *string
|
||||
userConfigFile string
|
||||
|
||||
openOnce sync.Once
|
||||
openErr error
|
||||
@ -62,11 +63,12 @@ var _ migrations.Fetcher = (*IpfsFetcher)(nil)
|
||||
// Bootstrap and peer information in read from the IPFS config file in
|
||||
// repoRoot, unless repoRoot is nil. If repoRoot is empty (""), then read the
|
||||
// config from the default IPFS directory.
|
||||
func NewIpfsFetcher(distPath string, fetchLimit int64, repoRoot *string) *IpfsFetcher {
|
||||
func NewIpfsFetcher(distPath string, fetchLimit int64, repoRoot *string, userConfigFile string) *IpfsFetcher {
|
||||
f := &IpfsFetcher{
|
||||
limit: defaultFetchLimit,
|
||||
distPath: migrations.LatestIpfsDist,
|
||||
repoRoot: repoRoot,
|
||||
limit: defaultFetchLimit,
|
||||
distPath: migrations.LatestIpfsDist,
|
||||
repoRoot: repoRoot,
|
||||
userConfigFile: userConfigFile,
|
||||
}
|
||||
|
||||
if distPath != "" {
|
||||
@ -92,7 +94,7 @@ func (f *IpfsFetcher) Fetch(ctx context.Context, filePath string) ([]byte, error
|
||||
// Initialize and start IPFS node on first call to Fetch, since the fetcher
|
||||
// may be created by not used.
|
||||
f.openOnce.Do(func() {
|
||||
bootstrap, peers := readIpfsConfig(f.repoRoot)
|
||||
bootstrap, peers := readIpfsConfig(f.repoRoot, f.userConfigFile)
|
||||
f.ipfsTmpDir, f.openErr = initTempNode(ctx, bootstrap, peers)
|
||||
if f.openErr != nil {
|
||||
return
|
||||
@ -288,12 +290,12 @@ func parsePath(fetchPath string) (ipath.Path, error) {
|
||||
return ipfsPath, ipfsPath.IsValid()
|
||||
}
|
||||
|
||||
func readIpfsConfig(repoRoot *string) (bootstrap []string, peers []peer.AddrInfo) {
|
||||
func readIpfsConfig(repoRoot *string, userConfigFile string) (bootstrap []string, peers []peer.AddrInfo) {
|
||||
if repoRoot == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cfgPath, err := config.Filename(*repoRoot)
|
||||
cfgPath, err := config.Filename(*repoRoot, userConfigFile)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
|
@ -26,7 +26,7 @@ func TestIpfsFetcher(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
fetcher := NewIpfsFetcher("", 0, nil)
|
||||
fetcher := NewIpfsFetcher("", 0, nil, "")
|
||||
defer fetcher.Close()
|
||||
|
||||
out, err := fetcher.Fetch(ctx, "go-ipfs/versions")
|
||||
@ -62,7 +62,7 @@ func TestInitIpfsFetcher(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
f := NewIpfsFetcher("", 0, nil)
|
||||
f := NewIpfsFetcher("", 0, nil, "")
|
||||
defer f.Close()
|
||||
|
||||
// Init ipfs repo
|
||||
@ -132,7 +132,7 @@ func TestReadIpfsConfig(t *testing.T) {
|
||||
`
|
||||
|
||||
noSuchDir := "no_such_dir-5953aa51-1145-4efd-afd1-a069075fcf76"
|
||||
bootstrap, peers := readIpfsConfig(&noSuchDir)
|
||||
bootstrap, peers := readIpfsConfig(&noSuchDir, "")
|
||||
if bootstrap != nil {
|
||||
t.Error("expected nil bootstrap")
|
||||
}
|
||||
@ -142,12 +142,12 @@ func TestReadIpfsConfig(t *testing.T) {
|
||||
|
||||
tmpDir := makeConfig(t, testConfig)
|
||||
|
||||
bootstrap, peers = readIpfsConfig(nil)
|
||||
bootstrap, peers = readIpfsConfig(nil, "")
|
||||
if bootstrap != nil || peers != nil {
|
||||
t.Fatal("expected nil ipfs config items")
|
||||
}
|
||||
|
||||
bootstrap, peers = readIpfsConfig(&tmpDir)
|
||||
bootstrap, peers = readIpfsConfig(&tmpDir, "")
|
||||
if len(bootstrap) != 2 {
|
||||
t.Fatal("wrong number of bootstrap addresses")
|
||||
}
|
||||
@ -189,7 +189,7 @@ func TestBadBootstrappingIpfsConfig(t *testing.T) {
|
||||
|
||||
tmpDir := makeConfig(t, configBadBootstrap)
|
||||
|
||||
bootstrap, peers := readIpfsConfig(&tmpDir)
|
||||
bootstrap, peers := readIpfsConfig(&tmpDir, "")
|
||||
if bootstrap != nil {
|
||||
t.Fatal("expected nil bootstrap")
|
||||
}
|
||||
@ -219,7 +219,7 @@ func TestBadPeersIpfsConfig(t *testing.T) {
|
||||
|
||||
tmpDir := makeConfig(t, configBadPeers)
|
||||
|
||||
bootstrap, peers := readIpfsConfig(&tmpDir)
|
||||
bootstrap, peers := readIpfsConfig(&tmpDir, "")
|
||||
if peers != nil {
|
||||
t.Fatal("expected nil peers")
|
||||
}
|
||||
|
@ -115,12 +115,12 @@ func ExeName(name string) string {
|
||||
// ReadMigrationConfig reads the Migration section of the IPFS config, avoiding
|
||||
// reading anything other than the Migration section. That way, we're free to
|
||||
// make arbitrary changes to all _other_ sections in migrations.
|
||||
func ReadMigrationConfig(repoRoot string) (*config.Migration, error) {
|
||||
func ReadMigrationConfig(repoRoot string, userConfigFile string) (*config.Migration, error) {
|
||||
var cfg struct {
|
||||
Migration config.Migration
|
||||
}
|
||||
|
||||
cfgPath, err := config.Filename(repoRoot)
|
||||
cfgPath, err := config.Filename(repoRoot, userConfigFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -221,7 +221,7 @@ var testConfig = `
|
||||
func TestReadMigrationConfigDefaults(t *testing.T) {
|
||||
tmpDir := makeConfig(t, "{}")
|
||||
|
||||
cfg, err := ReadMigrationConfig(tmpDir)
|
||||
cfg, err := ReadMigrationConfig(tmpDir, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -243,7 +243,7 @@ func TestReadMigrationConfigDefaults(t *testing.T) {
|
||||
func TestReadMigrationConfigErrors(t *testing.T) {
|
||||
tmpDir := makeConfig(t, `{"Migration": {"Keep": "badvalue"}}`)
|
||||
|
||||
_, err := ReadMigrationConfig(tmpDir)
|
||||
_, err := ReadMigrationConfig(tmpDir, "")
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
@ -252,13 +252,13 @@ func TestReadMigrationConfigErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
os.RemoveAll(tmpDir)
|
||||
_, err = ReadMigrationConfig(tmpDir)
|
||||
_, err = ReadMigrationConfig(tmpDir, "")
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
|
||||
tmpDir = makeConfig(t, `}{`)
|
||||
_, err = ReadMigrationConfig(tmpDir)
|
||||
_, err = ReadMigrationConfig(tmpDir, "")
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
@ -267,7 +267,7 @@ func TestReadMigrationConfigErrors(t *testing.T) {
|
||||
func TestReadMigrationConfig(t *testing.T) {
|
||||
tmpDir := makeConfig(t, testConfig)
|
||||
|
||||
cfg, err := ReadMigrationConfig(tmpDir)
|
||||
cfg, err := ReadMigrationConfig(tmpDir, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
Reference in New Issue
Block a user