Unverified 提交 f4fd369d 作者: Whyrusleeping 提交者: GitHub

Merge pull request #4195 from ipfs/feat/config-patch

config: command to apply profile after init
...@@ -160,7 +160,7 @@ func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int, con ...@@ -160,7 +160,7 @@ func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int, con
} }
for _, profile := range confProfiles { for _, profile := range confProfiles {
transformer, ok := config.ConfigProfiles[profile] transformer, ok := config.Profiles[profile]
if !ok { if !ok {
return fmt.Errorf("invalid configuration profile: %s", profile) return fmt.Errorf("invalid configuration profile: %s", profile)
} }
......
...@@ -142,6 +142,7 @@ Set the value of the 'Datastore.Path' key: ...@@ -142,6 +142,7 @@ Set the value of the 'Datastore.Path' key:
"show": configShowCmd, "show": configShowCmd,
"edit": configEditCmd, "edit": configEditCmd,
"replace": configReplaceCmd, "replace": configReplaceCmd,
"profile": configProfileCmd,
}, },
} }
...@@ -293,6 +294,64 @@ can't be undone. ...@@ -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) { func getConfig(r repo.Repo, key string) (*ConfigField, error) {
value, err := r.GetConfigKey(key) value, err := r.GetConfigKey(key)
if err != nil { if err != nil {
......
...@@ -4,6 +4,46 @@ The go-ipfs config file is a json document. It is read once at node instantiatio ...@@ -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 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. 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 ## Table of Contents
- [`Addresses`](#addresses) - [`Addresses`](#addresses)
......
...@@ -28,17 +28,7 @@ func Init(out io.Writer, nBitsForKeypair int) (*Config, error) { ...@@ -28,17 +28,7 @@ func Init(out io.Writer, nBitsForKeypair int) (*Config, error) {
// setup the node's default addresses. // setup the node's default addresses.
// NOTE: two swarm listen addrs, one tcp, one utp. // NOTE: two swarm listen addrs, one tcp, one utp.
Addresses: Addresses{ Addresses: addressesConfig(),
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",
},
Datastore: datastore, Datastore: datastore,
Bootstrap: BootstrapPeerStrings(bootstrapPeers), Bootstrap: BootstrapPeerStrings(bootstrapPeers),
...@@ -97,6 +87,20 @@ const DefaultConnMgrLowWater = 600 ...@@ -97,6 +87,20 @@ const DefaultConnMgrLowWater = 600
// grace period // grace period
const DefaultConnMgrGracePeriod = time.Second * 20 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. // DefaultDatastoreConfig is an internal function exported to aid in testing.
func DefaultDatastoreConfig() Datastore { func DefaultDatastoreConfig() Datastore {
return Datastore{ return Datastore{
......
package config package config
// ConfigProfiles is a map holding configuration transformers // Transformer is a function which takes configuration and applies some filter to it
var ConfigProfiles = map[string]func(*Config) error{ type Transformer func(c *Config) error
"server": func(c *Config) error {
// defaultServerFilters has a list of non-routable IPv4 prefixes // 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 // according to http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
defaultServerFilters := []string{ var defaultServerFilters = []string{
"/ip4/10.0.0.0/ipcidr/8", "/ip4/10.0.0.0/ipcidr/8",
"/ip4/100.64.0.0/ipcidr/10", "/ip4/100.64.0.0/ipcidr/10",
"/ip4/169.254.0.0/ipcidr/16", "/ip4/169.254.0.0/ipcidr/16",
"/ip4/172.16.0.0/ipcidr/12", "/ip4/172.16.0.0/ipcidr/12",
"/ip4/192.0.0.0/ipcidr/24", "/ip4/192.0.0.0/ipcidr/24",
"/ip4/192.0.0.0/ipcidr/29", "/ip4/192.0.0.0/ipcidr/29",
"/ip4/192.0.0.8/ipcidr/32", "/ip4/192.0.0.8/ipcidr/32",
"/ip4/192.0.0.170/ipcidr/32", "/ip4/192.0.0.170/ipcidr/32",
"/ip4/192.0.0.171/ipcidr/32", "/ip4/192.0.0.171/ipcidr/32",
"/ip4/192.0.2.0/ipcidr/24", "/ip4/192.0.2.0/ipcidr/24",
"/ip4/192.168.0.0/ipcidr/16", "/ip4/192.168.0.0/ipcidr/16",
"/ip4/198.18.0.0/ipcidr/15", "/ip4/198.18.0.0/ipcidr/15",
"/ip4/198.51.100.0/ipcidr/24", "/ip4/198.51.100.0/ipcidr/24",
"/ip4/203.0.113.0/ipcidr/24", "/ip4/203.0.113.0/ipcidr/24",
"/ip4/240.0.0.0/ipcidr/4", "/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 c.Discovery.MDNS.Enabled = false
return nil 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 { "test": func(c *Config) error {
c.Addresses.API = "/ip4/127.0.0.1/tcp/0" c.Addresses.API = "/ip4/127.0.0.1/tcp/0"
c.Addresses.Gateway = "/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{ c.Addresses.Swarm = []string{
"/ip4/127.0.0.1/tcp/0", "/ip4/127.0.0.1/tcp/0",
} }
c.Swarm.DisableNatPortMap = true
c.Bootstrap = []string{} c.Bootstrap = []string{}
c.Discovery.MDNS.Enabled = false c.Discovery.MDNS.Enabled = false
return nil 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 { "badgerds": func(c *Config) error {
c.Datastore.Spec = map[string]interface{}{ c.Datastore.Spec = map[string]interface{}{
"type": "measure", "type": "measure",
...@@ -53,4 +69,38 @@ var ConfigProfiles = map[string]func(*Config) error{ ...@@ -53,4 +69,38 @@ var ConfigProfiles = map[string]func(*Config) error{
} }
return nil 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
} }
...@@ -480,6 +480,32 @@ func (r *FSRepo) FileManager() *filestore.FileManager { ...@@ -480,6 +480,32 @@ func (r *FSRepo) FileManager() *filestore.FileManager {
return r.filemgr 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. // setConfigUnsynced is for private use.
func (r *FSRepo) setConfigUnsynced(updated *config.Config) error { func (r *FSRepo) setConfigUnsynced(updated *config.Config) error {
configFilename, err := config.Filename(r.path) configFilename, err := config.Filename(r.path)
......
...@@ -28,6 +28,10 @@ func (m *Mock) SetConfig(updated *config.Config) error { ...@@ -28,6 +28,10 @@ func (m *Mock) SetConfig(updated *config.Config) error {
return nil return nil
} }
func (m *Mock) BackupConfig(prefix string) (string, error) {
return "", errTODO
}
func (m *Mock) SetConfigKey(key string, value interface{}) error { func (m *Mock) SetConfigKey(key string, value interface{}) error {
return errTODO return errTODO
} }
......
...@@ -18,6 +18,7 @@ var ( ...@@ -18,6 +18,7 @@ var (
type Repo interface { type Repo interface {
Config() (*config.Config, error) Config() (*config.Config, error)
BackupConfig(prefix string) (string, error)
SetConfig(*config.Config) error SetConfig(*config.Config) error
SetConfigKey(key string, value interface{}) error SetConfigKey(key string, value interface{}) error
......
...@@ -48,6 +48,33 @@ CONFIG_SET_JSON_TEST='{ ...@@ -48,6 +48,33 @@ CONFIG_SET_JSON_TEST='{
} }
}' }'
test_profile_apply_revert() {
profile=$1
inverse_profile=$2
test_expect_success "save expected config" '
ipfs config show >expected
'
test_expect_success "'ipfs config profile apply ${profile}' works" '
ipfs config profile apply '${profile}'
'
test_expect_success "profile ${profile} changed something" '
ipfs config show >actual &&
test_must_fail test_cmp expected actual
'
test_expect_success "'ipfs config profile apply ${inverse_profile}' works" '
ipfs config profile apply '${inverse_profile}'
'
test_expect_success "config is back to previous state after ${inverse_profile} was applied" '
ipfs config show >actual &&
test_cmp expected actual
'
}
test_config_cmd() { test_config_cmd() {
test_config_cmd_set "beep" "boop" test_config_cmd_set "beep" "boop"
test_config_cmd_set "beep1" "boop2" test_config_cmd_set "beep1" "boop2"
...@@ -151,6 +178,50 @@ test_config_cmd() { ...@@ -151,6 +178,50 @@ test_config_cmd() {
echo "Error: setting private key with API is not supported" > replace_expected echo "Error: setting private key with API is not supported" > replace_expected
test_cmp replace_out replace_expected test_cmp replace_out replace_expected
' '
test_expect_success "'ipfs config Swarm.AddrFilters' looks good" '
ipfs config Swarm.AddrFilters > actual_config &&
test $(cat actual_config | wc -l) = 1
'
test_expect_success "copy ipfs config" '
cp "$IPFS_PATH/config" before_patch
'
test_expect_success "'ipfs config profile apply server' works" '
ipfs config profile apply server
'
test_expect_success "backup was created and looks good" '
test_cmp "$(find "$IPFS_PATH" -name "config-*")" before_patch
'
test_expect_success "'ipfs config Swarm.AddrFilters' looks good with server profile" '
ipfs config Swarm.AddrFilters > actual_config &&
test $(cat actual_config | wc -l) = 17
'
test_expect_success "'ipfs config profile apply local-discovery' works" '
ipfs config profile apply local-discovery
'
test_expect_success "'ipfs config Swarm.AddrFilters' looks good with applied local-discovery profile" '
ipfs config Swarm.AddrFilters > actual_config &&
test $(cat actual_config | wc -l) = 1
'
test_profile_apply_revert server local-discovery
# won't work as we already have this profile applied
# test_profile_apply_revert test
# won't work as it changes datastore definition, which makes ipfs not launch
# without converting first
# test_profile_apply_revert badgerds
test_expect_success "cleanup config backups" '
find "$IPFS_PATH" -name "config-*" -exec rm {} \;
'
} }
test_init_ipfs test_init_ipfs
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论