github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/core/commands/config.go (about)

     1  package commands
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"os"
    11  	"os/exec"
    12  
    13  	cmds "github.com/ipfs/go-ipfs/commands"
    14  	repo "github.com/ipfs/go-ipfs/repo"
    15  	config "github.com/ipfs/go-ipfs/repo/config"
    16  	fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
    17  	u "github.com/ipfs/go-ipfs/util"
    18  )
    19  
    20  type ConfigField struct {
    21  	Key   string
    22  	Value interface{}
    23  }
    24  
    25  var ConfigCmd = &cmds.Command{
    26  	Helptext: cmds.HelpText{
    27  		Tagline: "get and set IPFS config values",
    28  		Synopsis: `
    29  ipfs config <key>          - Get value of <key>
    30  ipfs config <key> <value>  - Set value of <key> to <value>
    31  ipfs config show           - Show config file
    32  ipfs config edit           - Edit config file in $EDITOR
    33  ipfs config replace <file> - Replaces the config file with <file>
    34  `,
    35  		ShortDescription: `
    36  ipfs config controls configuration variables. It works like 'git config'.
    37  The configuration values are stored in a config file inside your IPFS
    38  repository.`,
    39  		LongDescription: `
    40  ipfs config controls configuration variables. It works
    41  much like 'git config'. The configuration values are stored in a config
    42  file inside your IPFS repository.
    43  
    44  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 ~/.ipfs/datastore
    53  `,
    54  	},
    55  
    56  	Arguments: []cmds.Argument{
    57  		cmds.StringArg("key", true, false, "The key of the config entry (e.g. \"Addresses.API\")"),
    58  		cmds.StringArg("value", false, false, "The value to set the config entry to"),
    59  	},
    60  	Options: []cmds.Option{
    61  		cmds.BoolOption("bool", "Set a boolean value"),
    62  		cmds.BoolOption("json", "Parse stringified JSON"),
    63  	},
    64  	Run: func(req cmds.Request, res cmds.Response) {
    65  		args := req.Arguments()
    66  		key := args[0]
    67  
    68  		r, err := fsrepo.Open(req.InvocContext().ConfigRoot)
    69  		if err != nil {
    70  			res.SetError(err, cmds.ErrNormal)
    71  			return
    72  		}
    73  		defer r.Close()
    74  
    75  		var output *ConfigField
    76  		if len(args) == 2 {
    77  			value := args[1]
    78  
    79  			if parseJson, _, _ := req.Option("json").Bool(); parseJson {
    80  				var jsonVal interface{}
    81  				if err := json.Unmarshal([]byte(value), &jsonVal); err != nil {
    82  					err = fmt.Errorf("failed to unmarshal json. %s", err)
    83  					res.SetError(err, cmds.ErrNormal)
    84  					return
    85  				}
    86  
    87  				output, err = setConfig(r, key, jsonVal)
    88  			} else if isbool, _, _ := req.Option("bool").Bool(); isbool {
    89  				output, err = setConfig(r, key, value == "true")
    90  			} else {
    91  				output, err = setConfig(r, key, value)
    92  			}
    93  		} else {
    94  			output, err = getConfig(r, key)
    95  		}
    96  		if err != nil {
    97  			res.SetError(err, cmds.ErrNormal)
    98  			return
    99  		}
   100  		res.SetOutput(output)
   101  	},
   102  	Marshalers: cmds.MarshalerMap{
   103  		cmds.Text: func(res cmds.Response) (io.Reader, error) {
   104  			if len(res.Request().Arguments()) == 2 {
   105  				return nil, nil // dont output anything
   106  			}
   107  
   108  			v := res.Output()
   109  			if v == nil {
   110  				k := res.Request().Arguments()[0]
   111  				return nil, fmt.Errorf("config does not contain key: %s", k)
   112  			}
   113  			vf, ok := v.(*ConfigField)
   114  			if !ok {
   115  				return nil, u.ErrCast()
   116  			}
   117  
   118  			buf, err := config.HumanOutput(vf.Value)
   119  			if err != nil {
   120  				return nil, err
   121  			}
   122  			buf = append(buf, byte('\n'))
   123  			return bytes.NewReader(buf), nil
   124  		},
   125  	},
   126  	Type: ConfigField{},
   127  	Subcommands: map[string]*cmds.Command{
   128  		"show":    configShowCmd,
   129  		"edit":    configEditCmd,
   130  		"replace": configReplaceCmd,
   131  	},
   132  }
   133  
   134  var configShowCmd = &cmds.Command{
   135  	Helptext: cmds.HelpText{
   136  		Tagline: "Outputs the content of the config file",
   137  		ShortDescription: `
   138  WARNING: Your private key is stored in the config file, and it will be
   139  included in the output of this command.
   140  `,
   141  	},
   142  
   143  	Run: func(req cmds.Request, res cmds.Response) {
   144  		filename, err := config.Filename(req.InvocContext().ConfigRoot)
   145  		if err != nil {
   146  			res.SetError(err, cmds.ErrNormal)
   147  			return
   148  		}
   149  
   150  		output, err := showConfig(filename)
   151  		if err != nil {
   152  			res.SetError(err, cmds.ErrNormal)
   153  			return
   154  		}
   155  		res.SetOutput(output)
   156  	},
   157  }
   158  
   159  var configEditCmd = &cmds.Command{
   160  	Helptext: cmds.HelpText{
   161  		Tagline: "Opens the config file for editing in $EDITOR",
   162  		ShortDescription: `
   163  To use 'ipfs config edit', you must have the $EDITOR environment
   164  variable set to your preferred text editor.
   165  `,
   166  	},
   167  
   168  	Run: func(req cmds.Request, res cmds.Response) {
   169  		filename, err := config.Filename(req.InvocContext().ConfigRoot)
   170  		if err != nil {
   171  			res.SetError(err, cmds.ErrNormal)
   172  			return
   173  		}
   174  
   175  		err = editConfig(filename)
   176  		if err != nil {
   177  			res.SetError(err, cmds.ErrNormal)
   178  		}
   179  	},
   180  }
   181  
   182  var configReplaceCmd = &cmds.Command{
   183  	Helptext: cmds.HelpText{
   184  		Tagline: "Replaces the config with <file>",
   185  		ShortDescription: `
   186  Make sure to back up the config file first if neccessary, this operation
   187  can't be undone.
   188  `,
   189  	},
   190  
   191  	Arguments: []cmds.Argument{
   192  		cmds.FileArg("file", true, false, "The file to use as the new config"),
   193  	},
   194  	Run: func(req cmds.Request, res cmds.Response) {
   195  		r, err := fsrepo.Open(req.InvocContext().ConfigRoot)
   196  		if err != nil {
   197  			res.SetError(err, cmds.ErrNormal)
   198  			return
   199  		}
   200  		defer r.Close()
   201  
   202  		file, err := req.Files().NextFile()
   203  		if err != nil {
   204  			res.SetError(err, cmds.ErrNormal)
   205  			return
   206  		}
   207  		defer file.Close()
   208  
   209  		err = replaceConfig(r, file)
   210  		if err != nil {
   211  			res.SetError(err, cmds.ErrNormal)
   212  			return
   213  		}
   214  	},
   215  }
   216  
   217  func getConfig(r repo.Repo, key string) (*ConfigField, error) {
   218  	value, err := r.GetConfigKey(key)
   219  	if err != nil {
   220  		return nil, fmt.Errorf("Failed to get config value: %s", err)
   221  	}
   222  	return &ConfigField{
   223  		Key:   key,
   224  		Value: value,
   225  	}, nil
   226  }
   227  
   228  func setConfig(r repo.Repo, key string, value interface{}) (*ConfigField, error) {
   229  	err := r.SetConfigKey(key, value)
   230  	if err != nil {
   231  		return nil, fmt.Errorf("Failed to set config value: %s (maybe use --json?)", err)
   232  	}
   233  	return getConfig(r, key)
   234  }
   235  
   236  func showConfig(filename string) (io.Reader, error) {
   237  	// TODO maybe we should omit privkey so we don't accidentally leak it?
   238  
   239  	data, err := ioutil.ReadFile(filename)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	return bytes.NewReader(data), nil
   245  }
   246  
   247  func editConfig(filename string) error {
   248  	editor := os.Getenv("EDITOR")
   249  	if editor == "" {
   250  		return errors.New("ENV variable $EDITOR not set")
   251  	}
   252  
   253  	cmd := exec.Command("sh", "-c", editor+" "+filename)
   254  	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
   255  	return cmd.Run()
   256  }
   257  
   258  func replaceConfig(r repo.Repo, file io.Reader) error {
   259  	var cfg config.Config
   260  	if err := json.NewDecoder(file).Decode(&cfg); err != nil {
   261  		return errors.New("Failed to decode file as config")
   262  	}
   263  
   264  	return r.SetConfig(&cfg)
   265  }