config.go 5.28 KB
Newer Older
1 2 3
package commands

import (
4
	"bytes"
5
	"encoding/json"
6 7 8
	"errors"
	"fmt"
	"io"
9
	"io/ioutil"
10 11 12 13
	"os"
	"os/exec"

	cmds "github.com/jbenet/go-ipfs/commands"
14
	repo "github.com/jbenet/go-ipfs/repo"
15
	config "github.com/jbenet/go-ipfs/repo/config"
16
	fsrepo "github.com/jbenet/go-ipfs/repo/fsrepo"
17
	u "github.com/jbenet/go-ipfs/util"
18 19 20 21 22 23 24
)

type ConfigField struct {
	Key   string
	Value interface{}
}

25
var ConfigCmd = &cmds.Command{
26 27 28 29 30
	Helptext: cmds.HelpText{
		Tagline: "get and set IPFS config values",
		Synopsis: `
ipfs config <key>          - Get value of <key>
ipfs config <key> <value>  - Set value of <key> to <value>
31 32
ipfs config show           - Show config file
ipfs config edit           - Edit config file in $EDITOR
33
ipfs config replace <file> - Replaces the config file with <file>
34 35 36 37 38 39 40 41 42 43 44
`,
		ShortDescription: `
ipfs config controls configuration variables. It works like 'git config'.
The configuration values are stored in a config file inside your IPFS
repository.`,
		LongDescription: `
ipfs config controls configuration variables. It works
much like 'git config'. The configuration values are stored in a config
file inside your IPFS repository.

EXAMPLES:
45

46
Get the value of the 'datastore.path' key:
47

48
  ipfs config datastore.path
49

50
Set the value of the 'datastore.path' key:
51

52
  ipfs config datastore.path ~/.go-ipfs/datastore
53
`,
54
	},
55 56

	Arguments: []cmds.Argument{
57 58
		cmds.StringArg("key", true, false, "The key of the config entry (e.g. \"Addresses.API\")"),
		cmds.StringArg("value", false, false, "The value to set the config entry to"),
59
	},
60
	Run: func(req cmds.Request) (interface{}, error) {
61
		args := req.Arguments()
62
		key := args[0]
63

64 65
		r := fsrepo.At(req.Context().ConfigRoot)
		if err := r.Open(); err != nil {
66
			return nil, err
67
		}
68
		defer r.Close()
69 70 71

		var value string
		if len(args) == 2 {
72
			value = args[1]
73
			return setConfig(r, key, value)
74 75

		} else {
76
			return getConfig(r, key)
77 78
		}
	},
79
	Marshalers: cmds.MarshalerMap{
80
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
81
			if len(res.Request().Arguments()) == 2 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
82
				return nil, nil // dont output anything
83 84
			}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
85 86 87 88 89 90 91 92 93 94 95
			v := res.Output()
			if v == nil {
				k := res.Request().Arguments()[0]
				return nil, fmt.Errorf("config does not contain key: %s", k)
			}
			vf, ok := v.(*ConfigField)
			if !ok {
				return nil, u.ErrCast()
			}

			buf, err := config.HumanOutput(vf.Value)
96 97 98
			if err != nil {
				return nil, err
			}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
99
			buf = append(buf, byte('\n'))
100
			return bytes.NewReader(buf), nil
101 102
		},
	},
103
	Type: ConfigField{},
104
	Subcommands: map[string]*cmds.Command{
105 106 107
		"show":    configShowCmd,
		"edit":    configEditCmd,
		"replace": configReplaceCmd,
108 109 110 111
	},
}

var configShowCmd = &cmds.Command{
112 113 114 115
	Helptext: cmds.HelpText{
		Tagline: "Outputs the content of the config file",
		ShortDescription: `
WARNING: Your private key is stored in the config file, and it will be
116 117
included in the output of this command.
`,
118
	},
119

120
	Run: func(req cmds.Request) (interface{}, error) {
121 122
		filename, err := config.Filename(req.Context().ConfigRoot)
		if err != nil {
123
			return nil, err
124 125
		}

126
		return showConfig(filename)
127 128 129 130
	},
}

var configEditCmd = &cmds.Command{
131 132 133 134
	Helptext: cmds.HelpText{
		Tagline: "Opens the config file for editing in $EDITOR",
		ShortDescription: `
To use 'ipfs config edit', you must have the $EDITOR environment
135 136
variable set to your preferred text editor.
`,
137
	},
138

139
	Run: func(req cmds.Request) (interface{}, error) {
140 141
		filename, err := config.Filename(req.Context().ConfigRoot)
		if err != nil {
142
			return nil, err
143 144
		}

145
		return nil, editConfig(filename)
146 147 148
	},
}

149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
var configReplaceCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Replaces the config with <file>",
		ShortDescription: `
Make sure to back up the config file first if neccessary, this operation
can't be undone.
`,
	},

	Arguments: []cmds.Argument{
		cmds.FileArg("file", true, false, "The file to use as the new config"),
	},
	Run: func(req cmds.Request) (interface{}, error) {
		r := fsrepo.At(req.Context().ConfigRoot)
		if err := r.Open(); err != nil {
			return nil, err
		}
		defer r.Close()

		file, err := req.Files().NextFile()
		if err != nil {
			return nil, err
		}
		defer file.Close()

		return nil, replaceConfig(r, file)
	},
}

178
func getConfig(r repo.Repo, key string) (*ConfigField, error) {
179
	value, err := r.GetConfigKey(key)
180 181 182 183 184 185 186 187 188
	if err != nil {
		return nil, fmt.Errorf("Failed to get config value: %s", err)
	}
	return &ConfigField{
		Key:   key,
		Value: value,
	}, nil
}

189
func setConfig(r repo.Repo, key, value string) (*ConfigField, error) {
190
	err := r.SetConfigKey(key, value)
191 192 193
	if err != nil {
		return nil, fmt.Errorf("Failed to set config value: %s", err)
	}
194
	return getConfig(r, key)
195 196 197
}

func showConfig(filename string) (io.Reader, error) {
Brian Tiger Chow's avatar
todo  
Brian Tiger Chow committed
198
	// TODO maybe we should omit privkey so we don't accidentally leak it?
199

200
	data, err := ioutil.ReadFile(filename)
201 202 203 204
	if err != nil {
		return nil, err
	}

205
	return bytes.NewReader(data), nil
206 207 208 209 210 211 212 213 214 215 216 217
}

func editConfig(filename string) error {
	editor := os.Getenv("EDITOR")
	if editor == "" {
		return errors.New("ENV variable $EDITOR not set")
	}

	cmd := exec.Command("sh", "-c", editor+" "+filename)
	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
	return cmd.Run()
}
218 219 220 221 222 223 224 225 226

func replaceConfig(r repo.Repo, file io.Reader) error {
	var cfg config.Config
	if err := json.NewDecoder(file).Decode(&cfg); err != nil {
		return errors.New("Failed to decode file as config")
	}

	return r.SetConfig(&cfg)
}