Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

config: command to apply profile after init #4195

Merged
merged 11 commits into from
Jan 2, 2018
2 changes: 1 addition & 1 deletion cmd/ipfs/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int, con
}

for _, profile := range confProfiles {
transformer, ok := config.ConfigProfiles[profile]
transformer, ok := config.Profiles[profile]
if !ok {
return fmt.Errorf("invalid configuration profile: %s", profile)
}
Expand Down
59 changes: 59 additions & 0 deletions core/commands/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ Set the value of the 'Datastore.Path' key:
"show": configShowCmd,
"edit": configEditCmd,
"replace": configReplaceCmd,
"profile": configProfileCmd,
},
}

Expand Down Expand Up @@ -293,6 +294,64 @@ can't be undone.
},
}

var configProfileCmd = &cmds.Command{
Helptext: cmdkit.HelpText{
Tagline: "Apply profiles to config.",
},

Subcommands: map[string]*cmds.Command{
"apply": configProfileApplyCmd,
},
}

var configProfileApplyCmd = &cmds.Command{
Helptext: cmdkit.HelpText{
Tagline: "Apply profile to config.",
},
Arguments: []cmdkit.Argument{
cmdkit.StringArg("profile", true, false, "The profile to apply to the config."),
},
Run: func(req cmds.Request, res cmds.Response) {
profile, ok := config.Profiles[req.Arguments()[0]]
if !ok {
res.SetError(fmt.Errorf("%s is not a profile", req.Arguments()[0]), cmdkit.ErrNormal)
return
}

err := transformConfig(req.InvocContext().ConfigRoot, req.Arguments()[0], profile)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}
res.SetOutput(nil)
},
}

func transformConfig(configRoot string, configName string, transformer config.Transformer) error {
r, err := fsrepo.Open(configRoot)
if err != nil {
return err
}
defer r.Close()

cfg, err := r.Config()
if err != nil {
return err
}

err = transformer(cfg)
if err != nil {
return err
}

_, err = r.BackupConfig("pre-" + configName + "-")
if err != nil {
return err
}

return r.SetConfig(cfg)
}

func getConfig(r repo.Repo, key string) (*ConfigField, error) {
value, err := r.GetConfigKey(key)
if err != nil {
Expand Down
40 changes: 40 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,46 @@ The go-ipfs config file is a json document. It is read once at node instantiatio
either for an offline command, or when starting the daemon. Commands that execute
on a running daemon do not read the config file at runtime.

#### Profiles
Configuration profiles allow to tweak configuration quickly. Profiles can be
applied with `--profile` flag to `ipfs init` or with `ipfs config profile apply`
command. When a profile is applied a backup of the configuration file will
be created in $IPFS_PATH

Available profiles:
- `server`

Recommended for nodes with public IPv4 address (servers, VPSes, etc.),
disables host and content discovery in local networks.

- `local-discovery`

Sets default values to fields affected by `server` profile, enables
discovery in local networks.

- `test`

Reduces external interference, useful for running ipfs in test environments.
Note that with these settings node won't be able to talk to the rest of the
network without manual bootstrap.

- `default-networking`

Restores default network settings. Inverse profile of the `test` profile.

- `badgerds`

Replaces default datastore configuration with experimental badger datastore.
If you apply this profile after `ipfs init`, you will need to convert your
datastore to the new configuration. You can do this using [ipfs-ds-convert](https://github.com/ipfs/ipfs-ds-convert)

WARNING: badger datastore is experimental. Make sure to backup your data
frequently.

- `default-datastore`

Restores default datastore configuration.

## Table of Contents

- [`Addresses`](#addresses)
Expand Down
26 changes: 15 additions & 11 deletions repo/config/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,7 @@ func Init(out io.Writer, nBitsForKeypair int) (*Config, error) {

// setup the node's default addresses.
// NOTE: two swarm listen addrs, one tcp, one utp.
Addresses: Addresses{
Swarm: []string{
"/ip4/0.0.0.0/tcp/4001",
// "/ip4/0.0.0.0/udp/4002/utp", // disabled for now.
"/ip6/::/tcp/4001",
},
Announce: []string{},
NoAnnounce: []string{},
API: "/ip4/127.0.0.1/tcp/5001",
Gateway: "/ip4/127.0.0.1/tcp/8080",
},
Addresses: addressesConfig(),

Datastore: datastore,
Bootstrap: BootstrapPeerStrings(bootstrapPeers),
Expand Down Expand Up @@ -97,6 +87,20 @@ const DefaultConnMgrLowWater = 600
// grace period
const DefaultConnMgrGracePeriod = time.Second * 20

func addressesConfig() Addresses {
return Addresses{
Swarm: []string{
"/ip4/0.0.0.0/tcp/4001",
// "/ip4/0.0.0.0/udp/4002/utp", // disabled for now.
"/ip6/::/tcp/4001",
},
Announce: []string{},
NoAnnounce: []string{},
API: "/ip4/127.0.0.1/tcp/5001",
Gateway: "/ip4/127.0.0.1/tcp/8080",
}
}

// DefaultDatastoreConfig is an internal function exported to aid in testing.
func DefaultDatastoreConfig() Datastore {
return Datastore{
Expand Down
100 changes: 75 additions & 25 deletions repo/config/profile.go
Original file line number Diff line number Diff line change
@@ -1,46 +1,62 @@
package config

// ConfigProfiles is a map holding configuration transformers
var ConfigProfiles = map[string]func(*Config) error{
"server": func(c *Config) error {
// Transformer is a function which takes configuration and applies some filter to it
type Transformer func(c *Config) error

// defaultServerFilters has a list of non-routable IPv4 prefixes
// according to http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
defaultServerFilters := []string{
"/ip4/10.0.0.0/ipcidr/8",
"/ip4/100.64.0.0/ipcidr/10",
"/ip4/169.254.0.0/ipcidr/16",
"/ip4/172.16.0.0/ipcidr/12",
"/ip4/192.0.0.0/ipcidr/24",
"/ip4/192.0.0.0/ipcidr/29",
"/ip4/192.0.0.8/ipcidr/32",
"/ip4/192.0.0.170/ipcidr/32",
"/ip4/192.0.0.171/ipcidr/32",
"/ip4/192.0.2.0/ipcidr/24",
"/ip4/192.168.0.0/ipcidr/16",
"/ip4/198.18.0.0/ipcidr/15",
"/ip4/198.51.100.0/ipcidr/24",
"/ip4/203.0.113.0/ipcidr/24",
"/ip4/240.0.0.0/ipcidr/4",
}
// defaultServerFilters has a list of non-routable IPv4 prefixes
// according to http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
var defaultServerFilters = []string{
"/ip4/10.0.0.0/ipcidr/8",
"/ip4/100.64.0.0/ipcidr/10",
"/ip4/169.254.0.0/ipcidr/16",
"/ip4/172.16.0.0/ipcidr/12",
"/ip4/192.0.0.0/ipcidr/24",
"/ip4/192.0.0.0/ipcidr/29",
"/ip4/192.0.0.8/ipcidr/32",
"/ip4/192.0.0.170/ipcidr/32",
"/ip4/192.0.0.171/ipcidr/32",
"/ip4/192.0.2.0/ipcidr/24",
"/ip4/192.168.0.0/ipcidr/16",
"/ip4/198.18.0.0/ipcidr/15",
"/ip4/198.51.100.0/ipcidr/24",
"/ip4/203.0.113.0/ipcidr/24",
"/ip4/240.0.0.0/ipcidr/4",
}

c.Swarm.AddrFilters = append(c.Swarm.AddrFilters, defaultServerFilters...)
// Profiles is a map holding configuration transformers. Docs are in docs/config.md
var Profiles = map[string]Transformer{
"server": func(c *Config) error {
c.Addresses.NoAnnounce = appendSingle(c.Addresses.NoAnnounce, defaultServerFilters)
c.Swarm.AddrFilters = appendSingle(c.Swarm.AddrFilters, defaultServerFilters)
c.Discovery.MDNS.Enabled = false
return nil
},
"local-discovery": func(c *Config) error {
c.Addresses.NoAnnounce = deleteEntries(c.Addresses.NoAnnounce, defaultServerFilters)
c.Swarm.AddrFilters = deleteEntries(c.Swarm.AddrFilters, defaultServerFilters)
c.Discovery.MDNS.Enabled = true
return nil
},
"test": func(c *Config) error {
c.Addresses.API = "/ip4/127.0.0.1/tcp/0"
c.Addresses.Gateway = "/ip4/127.0.0.1/tcp/0"

c.Swarm.DisableNatPortMap = true
c.Addresses.Swarm = []string{
"/ip4/127.0.0.1/tcp/0",
}

c.Swarm.DisableNatPortMap = true

c.Bootstrap = []string{}
c.Discovery.MDNS.Enabled = false
return nil
},
"default-networking": func(c *Config) error {
c.Addresses = addressesConfig()

c.Swarm.DisableNatPortMap = false
c.Discovery.MDNS.Enabled = true
return nil
},
"badgerds": func(c *Config) error {
c.Datastore.Spec = map[string]interface{}{
"type": "measure",
Expand All @@ -53,4 +69,38 @@ var ConfigProfiles = map[string]func(*Config) error{
}
return nil
},
"default-datastore": func(c *Config) error {
c.Datastore.Spec = DefaultDatastoreConfig().Spec
return nil
},
}

func appendSingle(a []string, b []string) []string {
m := map[string]struct{}{}
for _, f := range a {
m[f] = struct{}{}
}
for _, f := range b {
m[f] = struct{}{}
}
return mapKeys(m)
}

func deleteEntries(arr []string, del []string) []string {
m := map[string]struct{}{}
for _, f := range arr {
m[f] = struct{}{}
}
for _, f := range del {
delete(m, f)
}
return mapKeys(m)
}

func mapKeys(m map[string]struct{}) []string {
out := make([]string, 0, len(m))
for f := range m {
out = append(out, f)
}
return out
}
26 changes: 26 additions & 0 deletions repo/fsrepo/fsrepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,32 @@ func (r *FSRepo) FileManager() *filestore.FileManager {
return r.filemgr
}

func (r *FSRepo) BackupConfig(prefix string) (string, error) {
temp, err := ioutil.TempFile(r.path, "config-"+prefix)
if err != nil {
return "", err
}
defer temp.Close()

configFilename, err := config.Filename(r.path)
if err != nil {
return "", err
}

orig, err := os.OpenFile(configFilename, os.O_RDONLY, 0600)
if err != nil {
return "", err
}
defer orig.Close()

_, err = io.Copy(temp, orig)
if err != nil {
return "", err
}

return orig.Name(), nil
}

// setConfigUnsynced is for private use.
func (r *FSRepo) setConfigUnsynced(updated *config.Config) error {
configFilename, err := config.Filename(r.path)
Expand Down
4 changes: 4 additions & 0 deletions repo/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ func (m *Mock) SetConfig(updated *config.Config) error {
return nil
}

func (m *Mock) BackupConfig(prefix string) (string, error) {
return "", errTODO
}

func (m *Mock) SetConfigKey(key string, value interface{}) error {
return errTODO
}
Expand Down
1 change: 1 addition & 0 deletions repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var (

type Repo interface {
Config() (*config.Config, error)
BackupConfig(prefix string) (string, error)
SetConfig(*config.Config) error

SetConfigKey(key string, value interface{}) error
Expand Down
Loading