mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-28 13:57:52 +08:00
Merge pull request #4195 from ipfs/feat/config-patch
config: command to apply profile after init
This commit is contained in:
commit
f4fd369d86
@ -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)
|
||||
}
|
||||
|
||||
@ -142,6 +142,7 @@ Set the value of the 'Datastore.Path' key:
|
||||
"show": configShowCmd,
|
||||
"edit": configEditCmd,
|
||||
"replace": configReplaceCmd,
|
||||
"profile": configProfileCmd,
|
||||
},
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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),
|
||||
@ -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{
|
||||
|
||||
@ -1,46 +1,62 @@
|
||||
package config
|
||||
|
||||
// ConfigProfiles is a map holding configuration transformers
|
||||
var ConfigProfiles = map[string]func(*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
|
||||
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",
|
||||
}
|
||||
|
||||
// Profiles is a map holding configuration transformers. Docs are in docs/config.md
|
||||
var Profiles = map[string]Transformer{
|
||||
"server": 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",
|
||||
}
|
||||
|
||||
c.Swarm.AddrFilters = append(c.Swarm.AddrFilters, defaultServerFilters...)
|
||||
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",
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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_set "beep" "boop"
|
||||
test_config_cmd_set "beep1" "boop2"
|
||||
@ -151,6 +178,50 @@ test_config_cmd() {
|
||||
echo "Error: setting private key with API is not supported" > 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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user