diff --git a/.circleci/main.yml b/.circleci/main.yml index 78ff3d707db..9838b0f4de5 100644 --- a/.circleci/main.yml +++ b/.circleci/main.yml @@ -120,11 +120,18 @@ jobs: # make sure the examples run against the current version of go-ipfs go mod edit -replace github.com/ipfs/go-ipfs=./../../.. go mod tidy + + # use the internal config package when we test the current version of go-ipfs + sed -i.bak 's;"github.com/ipfs/go-ipfs-config";"github.com/ipfs/go-ipfs/config";' ./main.go + go test -v ./... # restore the go.mod and go.sum files to their original state mv go.mod.bak go.mod mv go.sum.bak go.sum + + # restore the main.go to its original state + mv main.go.bak main.go working_directory: ~/ipfs/go-ipfs/docs/examples/go-ipfs-as-a-library - run: diff --git a/README.md b/README.md index 8c279efe046..f8d17ad20a3 100644 --- a/README.md +++ b/README.md @@ -412,7 +412,6 @@ Listing of the main packages used in the IPFS ecosystem. There are also three sp | **Namesys** | | [`go-ipns`](//github.com/ipfs/go-ipns) | [![Travis CI](https://flat.badgen.net/travis/ipfs/go-ipns/master)](https://travis-ci.com/ipfs/go-ipns) | [![codecov](https://codecov.io/gh/ipfs/go-ipns/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/ipfs/go-ipns) | IPNS datastructures and validation logic | | **Repo** | -| [`go-ipfs-config`](//github.com/ipfs/go-ipfs-config) | [![Travis CI](https://flat.badgen.net/travis/ipfs/go-ipfs-config/master)](https://travis-ci.com/ipfs/go-ipfs-config) | [![codecov](https://codecov.io/gh/ipfs/go-ipfs-config/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/ipfs/go-ipfs-config) | go-ipfs config file definitions | | [`go-fs-lock`](//github.com/ipfs/go-fs-lock) | [![Travis CI](https://flat.badgen.net/travis/ipfs/go-fs-lock/master)](https://travis-ci.com/ipfs/go-fs-lock) | [![codecov](https://codecov.io/gh/ipfs/go-fs-lock/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/ipfs/go-fs-lock) | lockfile management functions | | [`fs-repo-migrations`](//github.com/ipfs/fs-repo-migrations) | [![Travis CI](https://flat.badgen.net/travis/ipfs/fs-repo-migrations/master)](https://travis-ci.com/ipfs/fs-repo-migrations) | [![codecov](https://codecov.io/gh/ipfs/fs-repo-migrations/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/ipfs/fs-repo-migrations) | repo migrations | | **IPLD** | diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index 5de7e056eac..a2c65b5b99e 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -17,10 +17,10 @@ import ( multierror "github.com/hashicorp/go-multierror" version "github.com/ipfs/go-ipfs" - config "github.com/ipfs/go-ipfs-config" - cserial "github.com/ipfs/go-ipfs-config/serialize" utilmain "github.com/ipfs/go-ipfs/cmd/ipfs/util" oldcmds "github.com/ipfs/go-ipfs/commands" + config "github.com/ipfs/go-ipfs/config" + cserial "github.com/ipfs/go-ipfs/config/serialize" "github.com/ipfs/go-ipfs/core" commands "github.com/ipfs/go-ipfs/core/commands" "github.com/ipfs/go-ipfs/core/coreapi" diff --git a/cmd/ipfs/init.go b/cmd/ipfs/init.go index de3ad1180fa..dfbf01bb3a6 100644 --- a/cmd/ipfs/init.go +++ b/cmd/ipfs/init.go @@ -19,8 +19,8 @@ import ( unixfs "github.com/ipfs/go-unixfs" cmds "github.com/ipfs/go-ipfs-cmds" - config "github.com/ipfs/go-ipfs-config" files "github.com/ipfs/go-ipfs-files" + config "github.com/ipfs/go-ipfs/config" options "github.com/ipfs/interface-go-ipfs-core/options" ) diff --git a/cmd/ipfs/main.go b/cmd/ipfs/main.go index d975d18971f..11b21c89905 100644 --- a/cmd/ipfs/main.go +++ b/cmd/ipfs/main.go @@ -24,8 +24,8 @@ import ( cmds "github.com/ipfs/go-ipfs-cmds" "github.com/ipfs/go-ipfs-cmds/cli" cmdhttp "github.com/ipfs/go-ipfs-cmds/http" - config "github.com/ipfs/go-ipfs-config" u "github.com/ipfs/go-ipfs-util" + config "github.com/ipfs/go-ipfs/config" logging "github.com/ipfs/go-log" loggables "github.com/libp2p/go-libp2p-loggables" ma "github.com/multiformats/go-multiaddr" diff --git a/cmd/ipfs/pinmfs.go b/cmd/ipfs/pinmfs.go index 8ea9d2dcc8e..5eadef5a7e8 100644 --- a/cmd/ipfs/pinmfs.go +++ b/cmd/ipfs/pinmfs.go @@ -13,7 +13,7 @@ import ( logging "github.com/ipfs/go-log" pinclient "github.com/ipfs/go-pinning-service-http-client" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" "github.com/ipfs/go-ipfs/core" ) diff --git a/cmd/ipfs/pinmfs_test.go b/cmd/ipfs/pinmfs_test.go index e38e196beb3..63c8af070ee 100644 --- a/cmd/ipfs/pinmfs_test.go +++ b/cmd/ipfs/pinmfs_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" ipld "github.com/ipfs/go-ipld-format" merkledag "github.com/ipfs/go-merkledag" "github.com/libp2p/go-libp2p-core/host" diff --git a/cmd/ipfswatch/main.go b/cmd/ipfswatch/main.go index e0bd00e17dc..f810ada12c8 100644 --- a/cmd/ipfswatch/main.go +++ b/cmd/ipfswatch/main.go @@ -1,3 +1,4 @@ +//go:build !plan9 // +build !plan9 package main @@ -18,8 +19,8 @@ import ( fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" fsnotify "github.com/fsnotify/fsnotify" - config "github.com/ipfs/go-ipfs-config" files "github.com/ipfs/go-ipfs-files" + config "github.com/ipfs/go-ipfs/config" process "github.com/jbenet/goprocess" homedir "github.com/mitchellh/go-homedir" ) diff --git a/commands/context.go b/commands/context.go index e43672eee49..984071a05cd 100644 --- a/commands/context.go +++ b/commands/context.go @@ -11,7 +11,7 @@ import ( loader "github.com/ipfs/go-ipfs/plugin/loader" cmds "github.com/ipfs/go-ipfs-cmds" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" logging "github.com/ipfs/go-log" coreiface "github.com/ipfs/interface-go-ipfs-core" options "github.com/ipfs/interface-go-ipfs-core/options" diff --git a/config/addresses.go b/config/addresses.go new file mode 100644 index 00000000000..709b28d5847 --- /dev/null +++ b/config/addresses.go @@ -0,0 +1,11 @@ +package config + +// Addresses stores the (string) multiaddr addresses for the node. +type Addresses struct { + Swarm []string // addresses for the swarm to listen on + Announce []string // swarm addresses to announce to the network, if len > 0 replaces auto detected addresses + AppendAnnounce []string // similar to Announce but doesn't overwride auto detected addresses, they are just appended + NoAnnounce []string // swarm addresses not to announce to the network + API Strings // address for the local API (RPC) + Gateway Strings // address to listen on for IPFS HTTP object gateway +} diff --git a/config/api.go b/config/api.go new file mode 100644 index 00000000000..b36b1080304 --- /dev/null +++ b/config/api.go @@ -0,0 +1,5 @@ +package config + +type API struct { + HTTPHeaders map[string][]string // HTTP headers to return with the API. +} diff --git a/config/autonat.go b/config/autonat.go new file mode 100644 index 00000000000..64856faa680 --- /dev/null +++ b/config/autonat.go @@ -0,0 +1,81 @@ +package config + +import ( + "fmt" +) + +// AutoNATServiceMode configures the ipfs node's AutoNAT service. +type AutoNATServiceMode int + +const ( + // AutoNATServiceUnset indicates that the user has not set the + // AutoNATService mode. + // + // When unset, nodes configured to be public DHT nodes will _also_ + // perform limited AutoNAT dialbacks. + AutoNATServiceUnset AutoNATServiceMode = iota + // AutoNATServiceEnabled indicates that the user has enabled the + // AutoNATService. + AutoNATServiceEnabled + // AutoNATServiceDisabled indicates that the user has disabled the + // AutoNATService. + AutoNATServiceDisabled +) + +func (m *AutoNATServiceMode) UnmarshalText(text []byte) error { + switch string(text) { + case "": + *m = AutoNATServiceUnset + case "enabled": + *m = AutoNATServiceEnabled + case "disabled": + *m = AutoNATServiceDisabled + default: + return fmt.Errorf("unknown autonat mode: %s", string(text)) + } + return nil +} + +func (m AutoNATServiceMode) MarshalText() ([]byte, error) { + switch m { + case AutoNATServiceUnset: + return nil, nil + case AutoNATServiceEnabled: + return []byte("enabled"), nil + case AutoNATServiceDisabled: + return []byte("disabled"), nil + default: + return nil, fmt.Errorf("unknown autonat mode: %d", m) + } +} + +// AutoNATConfig configures the node's AutoNAT subsystem. +type AutoNATConfig struct { + // ServiceMode configures the node's AutoNAT service mode. + ServiceMode AutoNATServiceMode `json:",omitempty"` + + // Throttle configures AutoNAT dialback throttling. + // + // If unset, the conservative libp2p defaults will be unset. To help the + // network, please consider setting this and increasing the limits. + // + // By default, the limits will be a total of 30 dialbacks, with a + // per-peer max of 3 peer, resetting every minute. + Throttle *AutoNATThrottleConfig `json:",omitempty"` +} + +// AutoNATThrottleConfig configures the throttle limites +type AutoNATThrottleConfig struct { + // GlobalLimit and PeerLimit sets the global and per-peer dialback + // limits. The AutoNAT service will only perform the specified number of + // dialbacks per interval. + // + // Setting either to 0 will disable the appropriate limit. + GlobalLimit, PeerLimit int + + // Interval specifies how frequently this node should reset the + // global/peer dialback limits. + // + // When unset, this defaults to 1 minute. + Interval OptionalDuration `json:",omitempty"` +} diff --git a/config/bootstrap_peers.go b/config/bootstrap_peers.go new file mode 100644 index 00000000000..e22c55fb8a7 --- /dev/null +++ b/config/bootstrap_peers.go @@ -0,0 +1,77 @@ +package config + +import ( + "errors" + "fmt" + + peer "github.com/libp2p/go-libp2p-core/peer" + ma "github.com/multiformats/go-multiaddr" +) + +// DefaultBootstrapAddresses are the hardcoded bootstrap addresses +// for IPFS. they are nodes run by the IPFS team. docs on these later. +// As with all p2p networks, bootstrap is an important security concern. +// +// NOTE: This is here -- and not inside cmd/ipfs/init.go -- because of an +// import dependency issue. TODO: move this into a config/default/ package. +var DefaultBootstrapAddresses = []string{ + "/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", + "/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa", + "/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", + "/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt", + "/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", // mars.i.ipfs.io + "/ip4/104.131.131.82/udp/4001/quic/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", // mars.i.ipfs.io +} + +// ErrInvalidPeerAddr signals an address is not a valid peer address. +var ErrInvalidPeerAddr = errors.New("invalid peer address") + +func (c *Config) BootstrapPeers() ([]peer.AddrInfo, error) { + return ParseBootstrapPeers(c.Bootstrap) +} + +// DefaultBootstrapPeers returns the (parsed) set of default bootstrap peers. +// if it fails, it returns a meaningful error for the user. +// This is here (and not inside cmd/ipfs/init) because of module dependency problems. +func DefaultBootstrapPeers() ([]peer.AddrInfo, error) { + ps, err := ParseBootstrapPeers(DefaultBootstrapAddresses) + if err != nil { + return nil, fmt.Errorf(`failed to parse hardcoded bootstrap peers: %s +This is a problem with the ipfs codebase. Please report it to the dev team`, err) + } + return ps, nil +} + +func (c *Config) SetBootstrapPeers(bps []peer.AddrInfo) { + c.Bootstrap = BootstrapPeerStrings(bps) +} + +// ParseBootstrapPeer parses a bootstrap list into a list of AddrInfos. +func ParseBootstrapPeers(addrs []string) ([]peer.AddrInfo, error) { + maddrs := make([]ma.Multiaddr, len(addrs)) + for i, addr := range addrs { + var err error + maddrs[i], err = ma.NewMultiaddr(addr) + if err != nil { + return nil, err + } + } + return peer.AddrInfosFromP2pAddrs(maddrs...) +} + +// BootstrapPeerStrings formats a list of AddrInfos as a bootstrap peer list +// suitable for serialization. +func BootstrapPeerStrings(bps []peer.AddrInfo) []string { + bpss := make([]string, 0, len(bps)) + for _, pi := range bps { + addrs, err := peer.AddrInfoToP2pAddrs(&pi) + if err != nil { + // programmer error. + panic(err) + } + for _, addr := range addrs { + bpss = append(bpss, addr.String()) + } + } + return bpss +} diff --git a/config/bootstrap_peers_test.go b/config/bootstrap_peers_test.go new file mode 100644 index 00000000000..eeea9b5fdc0 --- /dev/null +++ b/config/bootstrap_peers_test.go @@ -0,0 +1,24 @@ +package config + +import ( + "sort" + "testing" +) + +func TestBoostrapPeerStrings(t *testing.T) { + parsed, err := ParseBootstrapPeers(DefaultBootstrapAddresses) + if err != nil { + t.Fatal(err) + } + + formatted := BootstrapPeerStrings(parsed) + sort.Strings(formatted) + expected := append([]string{}, DefaultBootstrapAddresses...) + sort.Strings(expected) + + for i, s := range formatted { + if expected[i] != s { + t.Fatalf("expected %s, %s", expected[i], s) + } + } +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 00000000000..419a6a71f3a --- /dev/null +++ b/config/config.go @@ -0,0 +1,137 @@ +// package config implements the ipfs config file datastructures and utilities. +package config + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/mitchellh/go-homedir" +) + +// Config is used to load ipfs config files. +type Config struct { + Identity Identity // local node's peer identity + Datastore Datastore // local node's storage + Addresses Addresses // local node's addresses + Mounts Mounts // local node's mount points + Discovery Discovery // local node's discovery mechanisms + Routing Routing // local node's routing settings + Ipns Ipns // Ipns settings + Bootstrap []string // local nodes's bootstrap peer addresses + Gateway Gateway // local node's gateway server options + API API // local node's API settings + Swarm SwarmConfig + AutoNAT AutoNATConfig + Pubsub PubsubConfig + Peering Peering + DNS DNS + Migration Migration + + Provider Provider + Reprovider Reprovider + Experimental Experiments + Plugins Plugins + Pinning Pinning + + Internal Internal // experimental/unstable options +} + +const ( + // DefaultPathName is the default config dir name + DefaultPathName = ".ipfs" + // DefaultPathRoot is the path to the default config dir location. + DefaultPathRoot = "~/" + DefaultPathName + // DefaultConfigFile is the filename of the configuration file + DefaultConfigFile = "config" + // EnvDir is the environment variable used to change the path root. + EnvDir = "IPFS_PATH" +) + +// PathRoot returns the default configuration root directory +func PathRoot() (string, error) { + dir := os.Getenv(EnvDir) + var err error + if len(dir) == 0 { + dir, err = homedir.Expand(DefaultPathRoot) + } + return dir, err +} + +// Path returns the path `extension` relative to the configuration root. If an +// empty string is provided for `configroot`, the default root is used. +func Path(configroot, extension string) (string, error) { + if len(configroot) == 0 { + dir, err := PathRoot() + if err != nil { + return "", err + } + return filepath.Join(dir, extension), nil + + } + return filepath.Join(configroot, extension), nil +} + +// Filename returns the configuration file path given a configuration root +// directory. If the configuration root directory is empty, use the default one +func Filename(configroot string) (string, error) { + return Path(configroot, DefaultConfigFile) +} + +// HumanOutput gets a config value ready for printing +func HumanOutput(value interface{}) ([]byte, error) { + s, ok := value.(string) + if ok { + return []byte(strings.Trim(s, "\n")), nil + } + return Marshal(value) +} + +// Marshal configuration with JSON +func Marshal(value interface{}) ([]byte, error) { + // need to prettyprint, hence MarshalIndent, instead of Encoder + return json.MarshalIndent(value, "", " ") +} + +func FromMap(v map[string]interface{}) (*Config, error) { + buf := new(bytes.Buffer) + if err := json.NewEncoder(buf).Encode(v); err != nil { + return nil, err + } + var conf Config + if err := json.NewDecoder(buf).Decode(&conf); err != nil { + return nil, fmt.Errorf("failure to decode config: %s", err) + } + return &conf, nil +} + +func ToMap(conf *Config) (map[string]interface{}, error) { + buf := new(bytes.Buffer) + if err := json.NewEncoder(buf).Encode(conf); err != nil { + return nil, err + } + var m map[string]interface{} + if err := json.NewDecoder(buf).Decode(&m); err != nil { + return nil, fmt.Errorf("failure to decode config: %s", err) + } + return m, nil +} + +// Clone copies the config. Use when updating. +func (c *Config) Clone() (*Config, error) { + var newConfig Config + var buf bytes.Buffer + + if err := json.NewEncoder(&buf).Encode(c); err != nil { + return nil, fmt.Errorf("failure to encode config: %s", err) + } + + if err := json.NewDecoder(&buf).Decode(&newConfig); err != nil { + return nil, fmt.Errorf("failure to decode config: %s", err) + } + + return &newConfig, nil +} diff --git a/config/config_test.go b/config/config_test.go new file mode 100644 index 00000000000..dead06f8a23 --- /dev/null +++ b/config/config_test.go @@ -0,0 +1,29 @@ +package config + +import ( + "testing" +) + +func TestClone(t *testing.T) { + c := new(Config) + c.Identity.PeerID = "faketest" + c.API.HTTPHeaders = map[string][]string{"foo": {"bar"}} + + newCfg, err := c.Clone() + if err != nil { + t.Fatal(err) + } + if newCfg.Identity.PeerID != c.Identity.PeerID { + t.Fatal("peer ID not preserved") + } + + c.API.HTTPHeaders["foo"] = []string{"baz"} + if newCfg.API.HTTPHeaders["foo"][0] != "bar" { + t.Fatal("HTTP headers not preserved") + } + + delete(c.API.HTTPHeaders, "foo") + if newCfg.API.HTTPHeaders["foo"][0] != "bar" { + t.Fatal("HTTP headers not preserved") + } +} diff --git a/config/datastore.go b/config/datastore.go new file mode 100644 index 00000000000..2b2bcb51828 --- /dev/null +++ b/config/datastore.go @@ -0,0 +1,32 @@ +package config + +import ( + "encoding/json" +) + +// DefaultDataStoreDirectory is the directory to store all the local IPFS data. +const DefaultDataStoreDirectory = "datastore" + +// Datastore tracks the configuration of the datastore. +type Datastore struct { + StorageMax string // in B, kB, kiB, MB, ... + StorageGCWatermark int64 // in percentage to multiply on StorageMax + GCPeriod string // in ns, us, ms, s, m, h + + // deprecated fields, use Spec + Type string `json:",omitempty"` + Path string `json:",omitempty"` + NoSync bool `json:",omitempty"` + Params *json.RawMessage `json:",omitempty"` + + Spec map[string]interface{} + + HashOnRead bool + BloomFilterSize int +} + +// DataStorePath returns the default data store path given a configuration root +// (set an empty string to have the default configuration root) +func DataStorePath(configroot string) (string, error) { + return Path(configroot, DefaultDataStoreDirectory) +} diff --git a/config/discovery.go b/config/discovery.go new file mode 100644 index 00000000000..4fb8508f00a --- /dev/null +++ b/config/discovery.go @@ -0,0 +1,12 @@ +package config + +type Discovery struct { + MDNS MDNS +} + +type MDNS struct { + Enabled bool + + // Time in seconds between discovery rounds + Interval int +} diff --git a/config/dns.go b/config/dns.go new file mode 100644 index 00000000000..b0f7b2710b6 --- /dev/null +++ b/config/dns.go @@ -0,0 +1,17 @@ +package config + +// DNS specifies DNS resolution rules using custom resolvers +type DNS struct { + // Resolvers is a map of FQDNs to URLs for custom DNS resolution. + // URLs starting with `https://` indicate DoH endpoints. + // Support for other resolver types can be added in the future. + // https://en.wikipedia.org/wiki/Fully_qualified_domain_name + // https://en.wikipedia.org/wiki/DNS_over_HTTPS + // + // Example: + // - Custom resolver for ENS: `eth.` → `https://eth.link/dns-query` + // - Override the default OS resolver: `.` → `https://doh.applied-privacy.net/query` + Resolvers map[string]string + // MaxCacheTTL is the maximum duration DNS entries are valid in the cache. + MaxCacheTTL *OptionalDuration `json:",omitempty"` +} diff --git a/config/experiments.go b/config/experiments.go new file mode 100644 index 00000000000..dba0ea7139b --- /dev/null +++ b/config/experiments.go @@ -0,0 +1,12 @@ +package config + +type Experiments struct { + FilestoreEnabled bool + UrlstoreEnabled bool + ShardingEnabled bool `json:",omitempty"` // deprecated by autosharding: https://github.com/ipfs/go-ipfs/pull/8527 + GraphsyncEnabled bool + Libp2pStreamMounting bool + P2pHttpProxy bool + StrategicProviding bool + AcceleratedDHTClient bool +} diff --git a/config/gateway.go b/config/gateway.go new file mode 100644 index 00000000000..644467891c9 --- /dev/null +++ b/config/gateway.go @@ -0,0 +1,71 @@ +package config + +type GatewaySpec struct { + // Paths is explicit list of path prefixes that should be handled by + // this gateway. Example: `["/ipfs", "/ipns", "/api"]` + Paths []string + + // UseSubdomains indicates whether or not this gateway uses subdomains + // for IPFS resources instead of paths. That is: http://CID.ipfs.GATEWAY/... + // + // If this flag is set, any /ipns/$id and/or /ipfs/$id paths in PathPrefixes + // will be permanently redirected to http://$id.[ipns|ipfs].$gateway/. + // + // We do not support using both paths and subdomains for a single domain + // for security reasons (Origin isolation). + UseSubdomains bool + + // NoDNSLink configures this gateway to _not_ resolve DNSLink for the FQDN + // provided in `Host` HTTP header. + NoDNSLink bool +} + +// Gateway contains options for the HTTP gateway server. +type Gateway struct { + + // HTTPHeaders configures the headers that should be returned by this + // gateway. + HTTPHeaders map[string][]string // HTTP headers to return with the gateway + + // RootRedirect is the path to which requests to `/` on this gateway + // should be redirected. + RootRedirect string + + // Writable enables PUT/POST request handling by this gateway. Usually, + // writing is done through the API, not the gateway. + Writable bool + + // PathPrefixes is an array of acceptable url paths that a client can + // specify in X-Ipfs-Path-Prefix header. + // + // The X-Ipfs-Path-Prefix header is used to specify a base path to prepend + // to links in directory listings and for trailing-slash redirects. It is + // intended to be set by a frontend http proxy like nginx. + // + // Example: To mount blog.ipfs.io (a DNSLink site) at ipfs.io/blog + // set PathPrefixes to ["/blog"] and nginx config to translate paths + // and pass Host header (for DNSLink): + // location /blog/ { + // rewrite "^/blog(/.*)$" $1 break; + // proxy_set_header Host blog.ipfs.io; + // proxy_set_header X-Ipfs-Gateway-Prefix /blog; + // proxy_pass http://127.0.0.1:8080; + // } + PathPrefixes []string + + // FIXME: Not yet implemented + APICommands []string + + // NoFetch configures the gateway to _not_ fetch blocks in response to + // requests. + NoFetch bool + + // NoDNSLink configures the gateway to _not_ perform DNS TXT record + // lookups in response to requests with values in `Host` HTTP header. + // This flag can be overriden per FQDN in PublicGateways. + NoDNSLink bool + + // PublicGateways configures behavior of known public gateways. + // Each key is a fully qualified domain name (FQDN). + PublicGateways map[string]*GatewaySpec +} diff --git a/config/identity.go b/config/identity.go new file mode 100644 index 00000000000..f4e7c87200d --- /dev/null +++ b/config/identity.go @@ -0,0 +1,29 @@ +package config + +import ( + "encoding/base64" + + ic "github.com/libp2p/go-libp2p-core/crypto" +) + +const IdentityTag = "Identity" +const PrivKeyTag = "PrivKey" +const PrivKeySelector = IdentityTag + "." + PrivKeyTag + +// Identity tracks the configuration of the local node's identity. +type Identity struct { + PeerID string + PrivKey string `json:",omitempty"` +} + +// DecodePrivateKey is a helper to decode the users PrivateKey +func (i *Identity) DecodePrivateKey(passphrase string) (ic.PrivKey, error) { + pkb, err := base64.StdEncoding.DecodeString(i.PrivKey) + if err != nil { + return nil, err + } + + // currently storing key unencrypted. in the future we need to encrypt it. + // TODO(security) + return ic.UnmarshalPrivateKey(pkb) +} diff --git a/config/init.go b/config/init.go new file mode 100644 index 00000000000..8e54eaa5866 --- /dev/null +++ b/config/init.go @@ -0,0 +1,245 @@ +package config + +import ( + "crypto/rand" + "encoding/base64" + "fmt" + "io" + "time" + + "github.com/ipfs/interface-go-ipfs-core/options" + "github.com/libp2p/go-libp2p-core/crypto" + "github.com/libp2p/go-libp2p-core/peer" +) + +func Init(out io.Writer, nBitsForKeypair int) (*Config, error) { + identity, err := CreateIdentity(out, []options.KeyGenerateOption{options.Key.Size(nBitsForKeypair)}) + if err != nil { + return nil, err + } + + return InitWithIdentity(identity) +} + +func InitWithIdentity(identity Identity) (*Config, error) { + bootstrapPeers, err := DefaultBootstrapPeers() + if err != nil { + return nil, err + } + + datastore := DefaultDatastoreConfig() + + conf := &Config{ + API: API{ + HTTPHeaders: map[string][]string{}, + }, + + // setup the node's default addresses. + // NOTE: two swarm listen addrs, one tcp, one utp. + Addresses: addressesConfig(), + + Datastore: datastore, + Bootstrap: BootstrapPeerStrings(bootstrapPeers), + Identity: identity, + Discovery: Discovery{ + MDNS: MDNS{ + Enabled: true, + Interval: 10, + }, + }, + + Routing: Routing{ + Type: "dht", + }, + + // setup the node mount points. + Mounts: Mounts{ + IPFS: "/ipfs", + IPNS: "/ipns", + }, + + Ipns: Ipns{ + ResolveCacheSize: 128, + }, + + Gateway: Gateway{ + RootRedirect: "", + Writable: false, + NoFetch: false, + PathPrefixes: []string{}, + HTTPHeaders: map[string][]string{ + "Access-Control-Allow-Origin": {"*"}, + "Access-Control-Allow-Methods": {"GET"}, + "Access-Control-Allow-Headers": {"X-Requested-With", "Range", "User-Agent"}, + }, + APICommands: []string{}, + }, + Reprovider: Reprovider{ + Interval: "12h", + Strategy: "all", + }, + Swarm: SwarmConfig{ + ConnMgr: ConnMgr{ + LowWater: DefaultConnMgrLowWater, + HighWater: DefaultConnMgrHighWater, + GracePeriod: DefaultConnMgrGracePeriod.String(), + Type: "basic", + }, + }, + Pinning: Pinning{ + RemoteServices: map[string]RemotePinningService{}, + }, + DNS: DNS{ + Resolvers: map[string]string{}, + }, + Migration: Migration{ + DownloadSources: []string{}, + Keep: "", + }, + } + + return conf, nil +} + +// DefaultConnMgrHighWater is the default value for the connection managers +// 'high water' mark +const DefaultConnMgrHighWater = 900 + +// DefaultConnMgrLowWater is the default value for the connection managers 'low +// water' mark +const DefaultConnMgrLowWater = 600 + +// DefaultConnMgrGracePeriod is the default value for the connection managers +// grace period +const DefaultConnMgrGracePeriod = time.Second * 20 + +func addressesConfig() Addresses { + return Addresses{ + Swarm: []string{ + "/ip4/0.0.0.0/tcp/4001", + "/ip6/::/tcp/4001", + "/ip4/0.0.0.0/udp/4001/quic", + "/ip6/::/udp/4001/quic", + }, + Announce: []string{}, + AppendAnnounce: []string{}, + NoAnnounce: []string{}, + API: Strings{"/ip4/127.0.0.1/tcp/5001"}, + Gateway: Strings{"/ip4/127.0.0.1/tcp/8080"}, + } +} + +// DefaultDatastoreConfig is an internal function exported to aid in testing. +func DefaultDatastoreConfig() Datastore { + return Datastore{ + StorageMax: "10GB", + StorageGCWatermark: 90, // 90% + GCPeriod: "1h", + BloomFilterSize: 0, + Spec: flatfsSpec(), + } +} + +func badgerSpec() map[string]interface{} { + return map[string]interface{}{ + "type": "measure", + "prefix": "badger.datastore", + "child": map[string]interface{}{ + "type": "badgerds", + "path": "badgerds", + "syncWrites": false, + "truncate": true, + }, + } +} + +func flatfsSpec() map[string]interface{} { + return map[string]interface{}{ + "type": "mount", + "mounts": []interface{}{ + map[string]interface{}{ + "mountpoint": "/blocks", + "type": "measure", + "prefix": "flatfs.datastore", + "child": map[string]interface{}{ + "type": "flatfs", + "path": "blocks", + "sync": true, + "shardFunc": "/repo/flatfs/shard/v1/next-to-last/2", + }, + }, + map[string]interface{}{ + "mountpoint": "/", + "type": "measure", + "prefix": "leveldb.datastore", + "child": map[string]interface{}{ + "type": "levelds", + "path": "datastore", + "compression": "none", + }, + }, + }, + } +} + +// CreateIdentity initializes a new identity. +func CreateIdentity(out io.Writer, opts []options.KeyGenerateOption) (Identity, error) { + // TODO guard higher up + ident := Identity{} + + settings, err := options.KeyGenerateOptions(opts...) + if err != nil { + return ident, err + } + + var sk crypto.PrivKey + var pk crypto.PubKey + + switch settings.Algorithm { + case "rsa": + if settings.Size == -1 { + settings.Size = options.DefaultRSALen + } + + fmt.Fprintf(out, "generating %d-bit RSA keypair...", settings.Size) + + priv, pub, err := crypto.GenerateKeyPair(crypto.RSA, settings.Size) + if err != nil { + return ident, err + } + + sk = priv + pk = pub + case "ed25519": + if settings.Size != -1 { + return ident, fmt.Errorf("number of key bits does not apply when using ed25519 keys") + } + fmt.Fprintf(out, "generating ED25519 keypair...") + priv, pub, err := crypto.GenerateEd25519Key(rand.Reader) + if err != nil { + return ident, err + } + + sk = priv + pk = pub + default: + return ident, fmt.Errorf("unrecognized key type: %s", settings.Algorithm) + } + fmt.Fprintf(out, "done\n") + + // currently storing key unencrypted. in the future we need to encrypt it. + // TODO(security) + skbytes, err := crypto.MarshalPrivateKey(sk) + if err != nil { + return ident, err + } + ident.PrivKey = base64.StdEncoding.EncodeToString(skbytes) + + id, err := peer.IDFromPublicKey(pk) + if err != nil { + return ident, err + } + ident.PeerID = id.Pretty() + fmt.Fprintf(out, "peer identity: %s\n", ident.PeerID) + return ident, nil +} diff --git a/config/init_test.go b/config/init_test.go new file mode 100644 index 00000000000..3e66e60cd2c --- /dev/null +++ b/config/init_test.go @@ -0,0 +1,49 @@ +package config + +import ( + "bytes" + "testing" + + "github.com/ipfs/interface-go-ipfs-core/options" + crypto_pb "github.com/libp2p/go-libp2p-core/crypto/pb" +) + +func TestCreateIdentity(t *testing.T) { + writer := bytes.NewBuffer(nil) + id, err := CreateIdentity(writer, []options.KeyGenerateOption{options.Key.Type(options.Ed25519Key)}) + if err != nil { + t.Fatal(err) + } + pk, err := id.DecodePrivateKey("") + if err != nil { + t.Fatal(err) + } + if pk.Type() != crypto_pb.KeyType_Ed25519 { + t.Fatal("unexpected type:", pk.Type()) + } + + id, err = CreateIdentity(writer, []options.KeyGenerateOption{options.Key.Type(options.RSAKey)}) + if err != nil { + t.Fatal(err) + } + pk, err = id.DecodePrivateKey("") + if err != nil { + t.Fatal(err) + } + if pk.Type() != crypto_pb.KeyType_RSA { + t.Fatal("unexpected type:", pk.Type()) + } +} + +func TestCreateIdentityOptions(t *testing.T) { + var w bytes.Buffer + + // ed25519 keys with bit size must fail. + _, err := CreateIdentity(&w, []options.KeyGenerateOption{ + options.Key.Type(options.Ed25519Key), + options.Key.Size(2048), + }) + if err == nil { + t.Errorf("ed25519 keys cannot have a custom bit size") + } +} diff --git a/config/internal.go b/config/internal.go new file mode 100644 index 00000000000..dcd834e701c --- /dev/null +++ b/config/internal.go @@ -0,0 +1,15 @@ +package config + +type Internal struct { + // All marked as omitempty since we are expecting to make changes to all subcomponents of Internal + Bitswap *InternalBitswap `json:",omitempty"` + UnixFSShardingSizeThreshold *OptionalString `json:",omitempty"` + Libp2pForceReachability *OptionalString `json:",omitempty"` +} + +type InternalBitswap struct { + TaskWorkerCount OptionalInteger + EngineBlockstoreWorkerCount OptionalInteger + EngineTaskWorkerCount OptionalInteger + MaxOutstandingBytesPerPeer OptionalInteger +} diff --git a/config/ipns.go b/config/ipns.go new file mode 100644 index 00000000000..d5191088409 --- /dev/null +++ b/config/ipns.go @@ -0,0 +1,11 @@ +package config + +type Ipns struct { + RepublishPeriod string + RecordLifetime string + + ResolveCacheSize int + + // Enable namesys pubsub (--enable-namesys-pubsub) + UsePubsub Flag `json:",omitempty"` +} diff --git a/config/migration.go b/config/migration.go new file mode 100644 index 00000000000..27d4b3c7025 --- /dev/null +++ b/config/migration.go @@ -0,0 +1,17 @@ +package config + +const DefaultMigrationKeep = "cache" + +var DefaultMigrationDownloadSources = []string{"HTTPS", "IPFS"} + +// Migration configures how migrations are downloaded and if the downloads are +// added to IPFS locally +type Migration struct { + // Sources in order of preference, where "IPFS" means use IPFS and "HTTPS" + // means use default gateways. Any other values are interpreted as + // hostnames for custom gateways. Empty list means "use default sources". + DownloadSources []string + // Whether or not to keep the migration after downloading it. + // Options are "discard", "cache", "pin". Empty string for default. + Keep string +} diff --git a/config/migration_test.go b/config/migration_test.go new file mode 100644 index 00000000000..a6cbd4438e7 --- /dev/null +++ b/config/migration_test.go @@ -0,0 +1,34 @@ +package config + +import ( + "encoding/json" + "testing" +) + +func TestMigrationDecode(t *testing.T) { + str := ` + { + "DownloadSources": ["IPFS", "HTTP", "127.0.0.1"], + "Keep": "cache" + } + ` + + var cfg Migration + if err := json.Unmarshal([]byte(str), &cfg); err != nil { + t.Errorf("failed while unmarshalling migration struct: %s", err) + } + + if len(cfg.DownloadSources) != 3 { + t.Fatal("wrong number of DownloadSources") + } + expect := []string{"IPFS", "HTTP", "127.0.0.1"} + for i := range expect { + if cfg.DownloadSources[i] != expect[i] { + t.Errorf("wrong DownloadSource at %d", i) + } + } + + if cfg.Keep != "cache" { + t.Error("wrong value for Keep") + } +} diff --git a/config/mounts.go b/config/mounts.go new file mode 100644 index 00000000000..b23d30b2ef3 --- /dev/null +++ b/config/mounts.go @@ -0,0 +1,8 @@ +package config + +// Mounts stores the (string) mount points +type Mounts struct { + IPFS string + IPNS string + FuseAllowOther bool +} diff --git a/config/peering.go b/config/peering.go new file mode 100644 index 00000000000..242ce2d9898 --- /dev/null +++ b/config/peering.go @@ -0,0 +1,9 @@ +package config + +import "github.com/libp2p/go-libp2p-core/peer" + +// Peering configures the peering service. +type Peering struct { + // Peers lists the nodes to attempt to stay connected with. + Peers []peer.AddrInfo +} diff --git a/config/plugins.go b/config/plugins.go new file mode 100644 index 00000000000..08a1acb34f5 --- /dev/null +++ b/config/plugins.go @@ -0,0 +1,11 @@ +package config + +type Plugins struct { + Plugins map[string]Plugin + // TODO: Loader Path? Leaving that out for now due to security concerns. +} + +type Plugin struct { + Disabled bool + Config interface{} +} diff --git a/config/profile.go b/config/profile.go new file mode 100644 index 00000000000..cbc7c976453 --- /dev/null +++ b/config/profile.go @@ -0,0 +1,249 @@ +package config + +import ( + "fmt" + "net" + "time" +) + +// Transformer is a function which takes configuration and applies some filter to it +type Transformer func(c *Config) error + +// Profile contains the profile transformer the description of the profile +type Profile struct { + // Description briefly describes the functionality of the profile. + Description string + + // Transform takes ipfs configuration and applies the profile to it. + Transform Transformer + + // InitOnly specifies that this profile can only be applied on init. + InitOnly bool +} + +// defaultServerFilters has is a list of IPv4 and IPv6 prefixes that are private, local only, or unrouteable. +// according to https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml +// and https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-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.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", + "/ip6/100::/ipcidr/64", + "/ip6/2001:2::/ipcidr/48", + "/ip6/2001:db8::/ipcidr/32", + "/ip6/fc00::/ipcidr/7", + "/ip6/fe80::/ipcidr/10", +} + +// Profiles is a map holding configuration transformers. Docs are in docs/config.md +var Profiles = map[string]Profile{ + "server": { + Description: `Disables local host discovery, recommended when +running IPFS on machines with public IPv4 addresses.`, + + Transform: 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.Swarm.DisableNatPortMap = true + return nil + }, + }, + + "local-discovery": { + Description: `Sets default values to fields affected by the server +profile, enables discovery in local networks.`, + + Transform: 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 + c.Swarm.DisableNatPortMap = false + return nil + }, + }, + "test": { + Description: `Reduces external interference of IPFS daemon, this +is useful when using the daemon in test environments.`, + + Transform: func(c *Config) error { + c.Addresses.API = Strings{"/ip4/127.0.0.1/tcp/0"} + c.Addresses.Gateway = Strings{"/ip4/127.0.0.1/tcp/0"} + 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": { + Description: `Restores default network settings. +Inverse profile of the test profile.`, + + Transform: func(c *Config) error { + c.Addresses = addressesConfig() + + bootstrapPeers, err := DefaultBootstrapPeers() + if err != nil { + return err + } + c.Bootstrap = appendSingle(c.Bootstrap, BootstrapPeerStrings(bootstrapPeers)) + + c.Swarm.DisableNatPortMap = false + c.Discovery.MDNS.Enabled = true + return nil + }, + }, + "default-datastore": { + Description: `Configures the node to use the default datastore (flatfs). + +Read the "flatfs" profile description for more information on this datastore. + +This profile may only be applied when first initializing the node. +`, + + InitOnly: true, + Transform: func(c *Config) error { + c.Datastore.Spec = flatfsSpec() + return nil + }, + }, + "flatfs": { + Description: `Configures the node to use the flatfs datastore. + +This is the most battle-tested and reliable datastore. +You should use this datastore if: + +* You need a very simple and very reliable datastore, and you trust your + filesystem. This datastore stores each block as a separate file in the + underlying filesystem so it's unlikely to loose data unless there's an issue + with the underlying file system. +* You need to run garbage collection in a way that reclaims free space as soon as possible. +* You want to minimize memory usage. +* You are ok with the default speed of data import, or prefer to use --nocopy. + +This profile may only be applied when first initializing the node. +`, + + InitOnly: true, + Transform: func(c *Config) error { + c.Datastore.Spec = flatfsSpec() + return nil + }, + }, + "badgerds": { + Description: `Configures the node to use the experimental badger datastore. + +Use this datastore if some aspects of performance, +especially the speed of adding many gigabytes of files, are critical. +However, be aware that: + +* This datastore will not properly reclaim space when your datastore is + smaller than several gigabytes. If you run IPFS with --enable-gc, you plan + on storing very little data in your IPFS node, and disk usage is more + critical than performance, consider using flatfs. +* This datastore uses up to several gigabytes of memory. +* Good for medium-size datastores, but may run into performance issues + if your dataset is bigger than a terabyte. +* The current implementation is based on old badger 1.x + which is no longer supported by the upstream team. + +This profile may only be applied when first initializing the node.`, + + InitOnly: true, + Transform: func(c *Config) error { + c.Datastore.Spec = badgerSpec() + return nil + }, + }, + "lowpower": { + Description: `Reduces daemon overhead on the system. May affect node +functionality - performance of content discovery and data +fetching may be degraded. +`, + Transform: func(c *Config) error { + c.Routing.Type = "dhtclient" + c.AutoNAT.ServiceMode = AutoNATServiceDisabled + c.Reprovider.Interval = "0" + + c.Swarm.ConnMgr.LowWater = 20 + c.Swarm.ConnMgr.HighWater = 40 + c.Swarm.ConnMgr.GracePeriod = time.Minute.String() + return nil + }, + }, + "randomports": { + Description: `Use a random port number for swarm.`, + + Transform: func(c *Config) error { + port, err := getAvailablePort() + if err != nil { + return err + } + c.Addresses.Swarm = []string{ + fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", port), + fmt.Sprintf("/ip6/::/tcp/%d", port), + } + return nil + }, + }, +} + +func getAvailablePort() (port int, err error) { + ln, err := net.Listen("tcp", "[::]:0") + if err != nil { + return 0, err + } + defer ln.Close() + port = ln.Addr().(*net.TCPAddr).Port + return port, nil +} + +func appendSingle(a []string, b []string) []string { + out := make([]string, 0, len(a)+len(b)) + m := map[string]bool{} + for _, f := range a { + if !m[f] { + out = append(out, f) + } + m[f] = true + } + for _, f := range b { + if !m[f] { + out = append(out, f) + } + m[f] = true + } + return out +} + +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 +} diff --git a/config/provider.go b/config/provider.go new file mode 100644 index 00000000000..f2b5afe05b4 --- /dev/null +++ b/config/provider.go @@ -0,0 +1,5 @@ +package config + +type Provider struct { + Strategy string // Which keys to announce +} diff --git a/config/pubsub.go b/config/pubsub.go new file mode 100644 index 00000000000..aabc35a0e0f --- /dev/null +++ b/config/pubsub.go @@ -0,0 +1,14 @@ +package config + +type PubsubConfig struct { + // Router can be either floodsub (legacy) or gossipsub (new and + // backwards compatible). + Router string + + // DisableSigning disables message signing. Message signing is *enabled* + // by default. + DisableSigning bool + + // Enable pubsub (--enable-pubsub-experiment) + Enabled Flag `json:",omitempty"` +} diff --git a/config/remotepin.go b/config/remotepin.go new file mode 100644 index 00000000000..135aa664d17 --- /dev/null +++ b/config/remotepin.go @@ -0,0 +1,33 @@ +package config + +var ( + RemoteServicesPath = "Pinning.RemoteServices" + PinningConcealSelector = []string{"Pinning", "RemoteServices", "*", "API", "Key"} +) + +type Pinning struct { + RemoteServices map[string]RemotePinningService +} + +type RemotePinningService struct { + API RemotePinningServiceAPI + Policies RemotePinningServicePolicies +} + +type RemotePinningServiceAPI struct { + Endpoint string + Key string +} + +type RemotePinningServicePolicies struct { + MFS RemotePinningServiceMFSPolicy +} + +type RemotePinningServiceMFSPolicy struct { + // Enable enables watching for changes in MFS and re-pinning the MFS root cid whenever a change occurs. + Enable bool + // Name is the pin name for MFS. + PinName string + // RepinInterval determines the repin interval when the policy is enabled. In ns, us, ms, s, m, h. + RepinInterval string +} diff --git a/config/reprovider.go b/config/reprovider.go new file mode 100644 index 00000000000..fa029c2fc21 --- /dev/null +++ b/config/reprovider.go @@ -0,0 +1,6 @@ +package config + +type Reprovider struct { + Interval string // Time period to reprovide locally stored objects to the network + Strategy string // Which keys to announce +} diff --git a/config/routing.go b/config/routing.go new file mode 100644 index 00000000000..c6157ec9637 --- /dev/null +++ b/config/routing.go @@ -0,0 +1,9 @@ +package config + +// Routing defines configuration options for libp2p routing +type Routing struct { + // Type sets default daemon routing mode. + // + // Can be one of "dht", "dhtclient", "dhtserver", "none", or unset. + Type string +} diff --git a/config/serialize/serialize.go b/config/serialize/serialize.go new file mode 100644 index 00000000000..e51e9211575 --- /dev/null +++ b/config/serialize/serialize.go @@ -0,0 +1,72 @@ +package fsrepo + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/ipfs/go-ipfs/config" + + "github.com/facebookgo/atomicfile" +) + +// ErrNotInitialized is returned when we fail to read the config because the +// repo doesn't exist. +var ErrNotInitialized = errors.New("ipfs not initialized, please run 'ipfs init'") + +// ReadConfigFile reads the config from `filename` into `cfg`. +func ReadConfigFile(filename string, cfg interface{}) error { + f, err := os.Open(filename) + if err != nil { + if os.IsNotExist(err) { + err = ErrNotInitialized + } + return err + } + defer f.Close() + if err := json.NewDecoder(f).Decode(cfg); err != nil { + return fmt.Errorf("failure to decode config: %s", err) + } + return nil +} + +// WriteConfigFile writes the config from `cfg` into `filename`. +func WriteConfigFile(filename string, cfg interface{}) error { + err := os.MkdirAll(filepath.Dir(filename), 0755) + if err != nil { + return err + } + + f, err := atomicfile.New(filename, 0600) + if err != nil { + return err + } + defer f.Close() + + return encode(f, cfg) +} + +// encode configuration with JSON +func encode(w io.Writer, value interface{}) error { + // need to prettyprint, hence MarshalIndent, instead of Encoder + buf, err := config.Marshal(value) + if err != nil { + return err + } + _, err = w.Write(buf) + return err +} + +// Load reads given file and returns the read config, or error. +func Load(filename string) (*config.Config, error) { + var cfg config.Config + err := ReadConfigFile(filename, &cfg) + if err != nil { + return nil, err + } + + return &cfg, err +} diff --git a/config/serialize/serialize_test.go b/config/serialize/serialize_test.go new file mode 100644 index 00000000000..0c8e12f40c0 --- /dev/null +++ b/config/serialize/serialize_test.go @@ -0,0 +1,37 @@ +package fsrepo + +import ( + "os" + "runtime" + "testing" + + config "github.com/ipfs/go-ipfs/config" +) + +func TestConfig(t *testing.T) { + const filename = ".ipfsconfig" + cfgWritten := new(config.Config) + cfgWritten.Identity.PeerID = "faketest" + + err := WriteConfigFile(filename, cfgWritten) + if err != nil { + t.Fatal(err) + } + cfgRead, err := Load(filename) + if err != nil { + t.Fatal(err) + } + if cfgWritten.Identity.PeerID != cfgRead.Identity.PeerID { + t.Fatal() + } + st, err := os.Stat(filename) + if err != nil { + t.Fatalf("cannot stat config file: %v", err) + } + + if runtime.GOOS != "windows" { // see https://golang.org/src/os/types_windows.go + if g := st.Mode().Perm(); g&0117 != 0 { + t.Fatalf("config file should not be executable or accessible to world: %v", g) + } + } +} diff --git a/config/swarm.go b/config/swarm.go new file mode 100644 index 00000000000..d03406126e0 --- /dev/null +++ b/config/swarm.go @@ -0,0 +1,131 @@ +package config + +type SwarmConfig struct { + // AddrFilters specifies a set libp2p addresses that we should never + // dial or receive connections from. + AddrFilters []string + + // DisableBandwidthMetrics disables recording of bandwidth metrics for a + // slight reduction in memory usage. You probably don't need to set this + // flag. + DisableBandwidthMetrics bool + + // DisableNatPortMap turns off NAT port mapping (UPnP, etc.). + DisableNatPortMap bool + + // DisableRelay explicitly disables the relay transport. + // + // Deprecated: This flag is deprecated and is overridden by + // `Swarm.Transports.Relay` if specified. + DisableRelay bool `json:",omitempty"` + + // EnableRelayHop makes this node act as a public relay v1 + // + // Deprecated: The circuit v1 protocol is deprecated. + // Use `Swarm.RelayService` to configure the circuit v2 relay. + EnableRelayHop bool `json:",omitempty"` + + // EnableAutoRelay enables the "auto relay user" feature. + // Node will find and use advertised public relays when it determines that + // it's not reachable from the public internet. + // + // Deprecated: This flag is deprecated and is overriden by + // `Swarm.RelayClient.Enabled` if specified. + EnableAutoRelay bool `json:",omitempty"` + + // RelayClient controls the client side of "auto relay" feature. + // When enabled, the node will use relays if it is not publicly reachable. + RelayClient RelayClient + + // RelayService.* controls the "relay service". + // When enabled, node will provide a limited relay service to other peers. + RelayService RelayService + + // EnableHolePunching enables the hole punching service. + EnableHolePunching Flag `json:",omitempty"` + + // Transports contains flags to enable/disable libp2p transports. + Transports Transports + + // ConnMgr configures the connection manager. + ConnMgr ConnMgr +} + +type RelayClient struct { + // Enables the auto relay feature: will use relays if it is not publicly reachable. + Enabled Flag `json:",omitempty"` + + // StaticRelays configures static relays to use when this node is not + // publicly reachable. If set, auto relay will not try to find any + // other relay servers. + StaticRelays []string `json:",omitempty"` +} + +// RelayService configures the resources of the circuit v2 relay. +// For every field a reasonable default will be defined in go-ipfs. +type RelayService struct { + // Enables the limited relay service for other peers (circuit v2 relay). + Enabled Flag `json:",omitempty"` + + // ConnectionDurationLimit is the time limit before resetting a relayed connection. + ConnectionDurationLimit *OptionalDuration `json:",omitempty"` + // ConnectionDataLimit is the limit of data relayed (on each direction) before resetting the connection. + ConnectionDataLimit *OptionalInteger `json:",omitempty"` + + // ReservationTTL is the duration of a new (or refreshed reservation). + ReservationTTL *OptionalDuration `json:",omitempty"` + + // MaxReservations is the maximum number of active relay slots. + MaxReservations *OptionalInteger `json:",omitempty"` + // MaxCircuits is the maximum number of open relay connections for each peer; defaults to 16. + MaxCircuits *OptionalInteger `json:",omitempty"` + // BufferSize is the size of the relayed connection buffers. + BufferSize *OptionalInteger `json:",omitempty"` + + // MaxReservationsPerPeer is the maximum number of reservations originating from the same peer. + MaxReservationsPerPeer *OptionalInteger `json:",omitempty"` + // MaxReservationsPerIP is the maximum number of reservations originating from the same IP address. + MaxReservationsPerIP *OptionalInteger `json:",omitempty"` + // MaxReservationsPerASN is the maximum number of reservations origination from the same ASN. + MaxReservationsPerASN *OptionalInteger `json:",omitempty"` +} + +type Transports struct { + // Network specifies the base transports we'll use for dialing. To + // listen on a transport, add the transport to your Addresses.Swarm. + Network struct { + // All default to on. + QUIC Flag `json:",omitempty"` + TCP Flag `json:",omitempty"` + Websocket Flag `json:",omitempty"` + Relay Flag `json:",omitempty"` + } + + // Security specifies the transports used to encrypt insecure network + // transports. + Security struct { + // Defaults to 100. + TLS Priority `json:",omitempty"` + // Defaults to 200. + SECIO Priority `json:",omitempty"` + // Defaults to 300. + Noise Priority `json:",omitempty"` + } + + // Multiplexers specifies the transports used to multiplex multiple + // connections over a single duplex connection. + Multiplexers struct { + // Defaults to 100. + Yamux Priority `json:",omitempty"` + // Defaults to 200. + Mplex Priority `json:",omitempty"` + } +} + +// ConnMgr defines configuration options for the libp2p connection manager +type ConnMgr struct { + Type string + LowWater int + HighWater int + GracePeriod string +} diff --git a/config/types.go b/config/types.go new file mode 100644 index 00000000000..c33689c5b2a --- /dev/null +++ b/config/types.go @@ -0,0 +1,367 @@ +package config + +import ( + "encoding/json" + "fmt" + "strings" + "time" +) + +// Strings is a helper type that (un)marshals a single string to/from a single +// JSON string and a slice of strings to/from a JSON array of strings. +type Strings []string + +// UnmarshalJSON conforms to the json.Unmarshaler interface. +func (o *Strings) UnmarshalJSON(data []byte) error { + if data[0] == '[' { + return json.Unmarshal(data, (*[]string)(o)) + } + var value string + if err := json.Unmarshal(data, &value); err != nil { + return err + } + if len(value) == 0 { + *o = []string{} + } else { + *o = []string{value} + } + return nil +} + +// MarshalJSON conforms to the json.Marshaler interface. +func (o Strings) MarshalJSON() ([]byte, error) { + switch len(o) { + case 0: + return json.Marshal(nil) + case 1: + return json.Marshal(o[0]) + default: + return json.Marshal([]string(o)) + } +} + +var _ json.Unmarshaler = (*Strings)(nil) +var _ json.Marshaler = (*Strings)(nil) + +// Flag represents a ternary value: false (-1), default (0), or true (+1). +// +// When encoded in json, False is "false", Default is "null" (or empty), and True +// is "true". +type Flag int8 + +const ( + False Flag = -1 + Default Flag = 0 + True Flag = 1 +) + +// WithDefault resolves the value of the flag given the provided default value. +// +// Panics if Flag is an invalid value. +func (f Flag) WithDefault(defaultValue bool) bool { + switch f { + case False: + return false + case Default: + return defaultValue + case True: + return true + default: + panic(fmt.Sprintf("invalid flag value %d", f)) + } +} + +func (f Flag) MarshalJSON() ([]byte, error) { + switch f { + case Default: + return json.Marshal(nil) + case True: + return json.Marshal(true) + case False: + return json.Marshal(false) + default: + return nil, fmt.Errorf("invalid flag value: %d", f) + } +} + +func (f *Flag) UnmarshalJSON(input []byte) error { + switch string(input) { + case "null": + *f = Default + case "false": + *f = False + case "true": + *f = True + default: + return fmt.Errorf("failed to unmarshal %q into a flag: must be null/undefined, true, or false", string(input)) + } + return nil +} + +func (f Flag) String() string { + switch f { + case Default: + return "default" + case True: + return "true" + case False: + return "false" + default: + return fmt.Sprintf("", f) + } +} + +var _ json.Unmarshaler = (*Flag)(nil) +var _ json.Marshaler = (*Flag)(nil) + +// Priority represents a value with a priority where 0 means "default" and -1 +// means "disabled". +// +// When encoded in json, Default is encoded as "null" and Disabled is encoded as +// "false". +type Priority int64 + +const ( + DefaultPriority Priority = 0 + Disabled Priority = -1 +) + +// WithDefault resolves the priority with the given default. +// +// If defaultPriority is Default/0, this function will return 0. +// +// Panics if the priority has an invalid value (e.g., not DefaultPriority, +// Disabled, or > 0). +func (p Priority) WithDefault(defaultPriority Priority) (priority int64, enabled bool) { + switch p { + case Disabled: + return 0, false + case DefaultPriority: + switch defaultPriority { + case Disabled: + return 0, false + case DefaultPriority: + return 0, true + default: + if defaultPriority <= 0 { + panic(fmt.Sprintf("invalid priority %d < 0", int64(defaultPriority))) + } + return int64(defaultPriority), true + } + default: + if p <= 0 { + panic(fmt.Sprintf("invalid priority %d < 0", int64(p))) + } + return int64(p), true + } +} + +func (p Priority) MarshalJSON() ([]byte, error) { + // > 0 == Priority + if p > 0 { + return json.Marshal(int64(p)) + } + // <= 0 == special + switch p { + case DefaultPriority: + return json.Marshal(nil) + case Disabled: + return json.Marshal(false) + default: + return nil, fmt.Errorf("invalid priority value: %d", p) + } +} + +func (p *Priority) UnmarshalJSON(input []byte) error { + switch string(input) { + case "null", "undefined": + *p = DefaultPriority + case "false": + *p = Disabled + case "true": + return fmt.Errorf("'true' is not a valid priority") + default: + var priority int64 + err := json.Unmarshal(input, &priority) + if err != nil { + return err + } + if priority <= 0 { + return fmt.Errorf("priority must be positive: %d <= 0", priority) + } + *p = Priority(priority) + } + return nil +} + +func (p Priority) String() string { + if p > 0 { + return fmt.Sprintf("%d", p) + } + switch p { + case DefaultPriority: + return "default" + case Disabled: + return "false" + default: + return fmt.Sprintf("", p) + } +} + +var _ json.Unmarshaler = (*Priority)(nil) +var _ json.Marshaler = (*Priority)(nil) + +// OptionalDuration wraps time.Duration to provide json serialization and deserialization. +// +// NOTE: the zero value encodes to JSON nill +type OptionalDuration struct { + value *time.Duration +} + +func (d *OptionalDuration) UnmarshalJSON(input []byte) error { + switch string(input) { + case "null", "undefined", "\"null\"", "", "default", "\"\"", "\"default\"": + *d = OptionalDuration{} + return nil + default: + text := strings.Trim(string(input), "\"") + value, err := time.ParseDuration(text) + if err != nil { + return err + } + *d = OptionalDuration{value: &value} + return nil + } +} + +func (d *OptionalDuration) IsDefault() bool { + return d == nil || d.value == nil +} + +func (d *OptionalDuration) WithDefault(defaultValue time.Duration) time.Duration { + if d == nil || d.value == nil { + return defaultValue + } + return *d.value +} + +func (d OptionalDuration) MarshalJSON() ([]byte, error) { + if d.value == nil { + return json.Marshal(nil) + } + return json.Marshal(d.value.String()) +} + +func (d OptionalDuration) String() string { + if d.value == nil { + return "default" + } + return d.value.String() +} + +var _ json.Unmarshaler = (*OptionalDuration)(nil) +var _ json.Marshaler = (*OptionalDuration)(nil) + +// OptionalInteger represents an integer that has a default value +// +// When encoded in json, Default is encoded as "null" +type OptionalInteger struct { + value *int64 +} + +// WithDefault resolves the integer with the given default. +func (p *OptionalInteger) WithDefault(defaultValue int64) (value int64) { + if p == nil || p.value == nil { + return defaultValue + } + return *p.value +} + +// IsDefault returns if this is a default optional integer +func (p *OptionalInteger) IsDefault() bool { + return p == nil || p.value == nil +} + +func (p OptionalInteger) MarshalJSON() ([]byte, error) { + if p.value != nil { + return json.Marshal(p.value) + } + return json.Marshal(nil) +} + +func (p *OptionalInteger) UnmarshalJSON(input []byte) error { + switch string(input) { + case "null", "undefined": + *p = OptionalInteger{} + default: + var value int64 + err := json.Unmarshal(input, &value) + if err != nil { + return err + } + *p = OptionalInteger{value: &value} + } + return nil +} + +func (p OptionalInteger) String() string { + if p.value == nil { + return "default" + } + return fmt.Sprintf("%d", p.value) +} + +var _ json.Unmarshaler = (*OptionalInteger)(nil) +var _ json.Marshaler = (*OptionalInteger)(nil) + +// OptionalString represents a string that has a default value +// +// When encoded in json, Default is encoded as "null" +type OptionalString struct { + value *string +} + +// WithDefault resolves the integer with the given default. +func (p *OptionalString) WithDefault(defaultValue string) (value string) { + if p == nil || p.value == nil { + return defaultValue + } + return *p.value +} + +// IsDefault returns if this is a default optional integer +func (p *OptionalString) IsDefault() bool { + return p == nil || p.value == nil +} + +func (p OptionalString) MarshalJSON() ([]byte, error) { + if p.value != nil { + return json.Marshal(p.value) + } + return json.Marshal(nil) +} + +func (p *OptionalString) UnmarshalJSON(input []byte) error { + switch string(input) { + case "null", "undefined": + *p = OptionalString{} + default: + var value string + err := json.Unmarshal(input, &value) + if err != nil { + return err + } + *p = OptionalString{value: &value} + } + return nil +} + +func (p OptionalString) String() string { + if p.value == nil { + return "default" + } + return *p.value +} + +var _ json.Unmarshaler = (*OptionalInteger)(nil) +var _ json.Marshaler = (*OptionalInteger)(nil) diff --git a/config/types_test.go b/config/types_test.go new file mode 100644 index 00000000000..caef2b112c0 --- /dev/null +++ b/config/types_test.go @@ -0,0 +1,514 @@ +package config + +import ( + "bytes" + "encoding/json" + "testing" + "time" +) + +func TestOptionalDuration(t *testing.T) { + makeDurationPointer := func(d time.Duration) *time.Duration { return &d } + + t.Run("marshalling and unmarshalling", func(t *testing.T) { + out, err := json.Marshal(OptionalDuration{value: makeDurationPointer(time.Second)}) + if err != nil { + t.Fatal(err) + } + expected := "\"1s\"" + if string(out) != expected { + t.Fatalf("expected %s, got %s", expected, string(out)) + } + var d OptionalDuration + + if err := json.Unmarshal(out, &d); err != nil { + t.Fatal(err) + } + if *d.value != time.Second { + t.Fatal("expected a second") + } + }) + + t.Run("default value", func(t *testing.T) { + for _, jsonStr := range []string{"null", "\"null\"", "\"\"", "\"default\""} { + var d OptionalDuration + if !d.IsDefault() { + t.Fatal("expected value to be the default initially") + } + if err := json.Unmarshal([]byte(jsonStr), &d); err != nil { + t.Fatalf("%s failed to unmarshall with %s", jsonStr, err) + } + if dur := d.WithDefault(time.Hour); dur != time.Hour { + t.Fatalf("expected default value to be used, got %s", dur) + } + if !d.IsDefault() { + t.Fatal("expected value to be the default") + } + } + }) + + t.Run("omitempty with default value", func(t *testing.T) { + type Foo struct { + D *OptionalDuration `json:",omitempty"` + } + // marshall to JSON without empty field + out, err := json.Marshal(new(Foo)) + if err != nil { + t.Fatal(err) + } + if string(out) != "{}" { + t.Fatalf("expected omitempty to omit the duration, got %s", out) + } + // unmarshall missing value and get the default + var foo2 Foo + if err := json.Unmarshal(out, &foo2); err != nil { + t.Fatalf("%s failed to unmarshall with %s", string(out), err) + } + if dur := foo2.D.WithDefault(time.Hour); dur != time.Hour { + t.Fatalf("expected default value to be used, got %s", dur) + } + if !foo2.D.IsDefault() { + t.Fatal("expected value to be the default") + } + }) + + t.Run("roundtrip including the default values", func(t *testing.T) { + for jsonStr, goValue := range map[string]OptionalDuration{ + // there are various footguns user can hit, normalize them to the canonical default + "null": {}, // JSON null → default value + "\"null\"": {}, // JSON string "null" sent/set by "ipfs config" cli → default value + "\"default\"": {}, // explicit "default" as string + "\"\"": {}, // user removed custom value, empty string should also parse as default + "\"1s\"": {value: makeDurationPointer(time.Second)}, + "\"42h1m3s\"": {value: makeDurationPointer(42*time.Hour + 1*time.Minute + 3*time.Second)}, + } { + var d OptionalDuration + err := json.Unmarshal([]byte(jsonStr), &d) + if err != nil { + t.Fatal(err) + } + + if goValue.value == nil && d.value == nil { + } else if goValue.value == nil && d.value != nil { + t.Errorf("expected nil for %s, got %s", jsonStr, d) + } else if *d.value != *goValue.value { + t.Fatalf("expected %s for %s, got %s", goValue, jsonStr, d) + } + + // Test Reverse + out, err := json.Marshal(goValue) + if err != nil { + t.Fatal(err) + } + if goValue.value == nil { + if !bytes.Equal(out, []byte("null")) { + t.Fatalf("expected JSON null for %s, got %s", jsonStr, string(out)) + } + continue + } + if string(out) != jsonStr { + t.Fatalf("expected %s, got %s", jsonStr, string(out)) + } + } + }) + + t.Run("invalid duration values", func(t *testing.T) { + for _, invalid := range []string{ + "\"s\"", "\"1ę\"", "\"-1\"", "\"1H\"", "\"day\"", + } { + var d OptionalDuration + err := json.Unmarshal([]byte(invalid), &d) + if err == nil { + t.Errorf("expected to fail to decode %s as an OptionalDuration, got %s instead", invalid, d) + } + } + }) +} + +func TestOneStrings(t *testing.T) { + out, err := json.Marshal(Strings{"one"}) + if err != nil { + t.Fatal(err) + + } + expected := "\"one\"" + if string(out) != expected { + t.Fatalf("expected %s, got %s", expected, string(out)) + } +} + +func TestNoStrings(t *testing.T) { + out, err := json.Marshal(Strings{}) + if err != nil { + t.Fatal(err) + + } + expected := "null" + if string(out) != expected { + t.Fatalf("expected %s, got %s", expected, string(out)) + } +} + +func TestManyStrings(t *testing.T) { + out, err := json.Marshal(Strings{"one", "two"}) + if err != nil { + t.Fatal(err) + + } + expected := "[\"one\",\"two\"]" + if string(out) != expected { + t.Fatalf("expected %s, got %s", expected, string(out)) + } +} + +func TestFunkyStrings(t *testing.T) { + toParse := " [ \"one\", \"two\" ] " + var s Strings + if err := json.Unmarshal([]byte(toParse), &s); err != nil { + t.Fatal(err) + } + if len(s) != 2 || s[0] != "one" && s[1] != "two" { + t.Fatalf("unexpected result: %v", s) + } +} + +func TestFlag(t *testing.T) { + // make sure we have the right zero value. + var defaultFlag Flag + if defaultFlag != Default { + t.Errorf("expected default flag to be %q, got %q", Default, defaultFlag) + } + + if defaultFlag.WithDefault(true) != true { + t.Error("expected default & true to be true") + } + + if defaultFlag.WithDefault(false) != false { + t.Error("expected default & false to be false") + } + + if True.WithDefault(false) != true { + t.Error("default should only apply to default") + } + + if False.WithDefault(true) != false { + t.Error("default should only apply to default") + } + + if True.WithDefault(true) != true { + t.Error("true & true is true") + } + + if False.WithDefault(true) != false { + t.Error("false & false is false") + } + + for jsonStr, goValue := range map[string]Flag{ + "null": Default, + "true": True, + "false": False, + } { + var d Flag + err := json.Unmarshal([]byte(jsonStr), &d) + if err != nil { + t.Fatal(err) + } + if d != goValue { + t.Fatalf("expected %s, got %s", goValue, d) + } + + // Reverse + out, err := json.Marshal(goValue) + if err != nil { + t.Fatal(err) + } + if string(out) != jsonStr { + t.Fatalf("expected %s, got %s", jsonStr, string(out)) + } + } + + type Foo struct { + F Flag `json:",omitempty"` + } + out, err := json.Marshal(new(Foo)) + if err != nil { + t.Fatal(err) + } + expected := "{}" + if string(out) != expected { + t.Fatal("expected omitempty to omit the flag") + } +} + +func TestPriority(t *testing.T) { + // make sure we have the right zero value. + var defaultPriority Priority + if defaultPriority != DefaultPriority { + t.Errorf("expected default priority to be %q, got %q", DefaultPriority, defaultPriority) + } + + if _, ok := defaultPriority.WithDefault(Disabled); ok { + t.Error("should have been disabled") + } + + if p, ok := defaultPriority.WithDefault(1); !ok || p != 1 { + t.Errorf("priority should have been 1, got %d", p) + } + + if p, ok := defaultPriority.WithDefault(DefaultPriority); !ok || p != 0 { + t.Errorf("priority should have been 0, got %d", p) + } + + for jsonStr, goValue := range map[string]Priority{ + "null": DefaultPriority, + "false": Disabled, + "1": 1, + "2": 2, + "100": 100, + } { + var d Priority + err := json.Unmarshal([]byte(jsonStr), &d) + if err != nil { + t.Fatal(err) + } + if d != goValue { + t.Fatalf("expected %s, got %s", goValue, d) + } + + // Reverse + out, err := json.Marshal(goValue) + if err != nil { + t.Fatal(err) + } + if string(out) != jsonStr { + t.Fatalf("expected %s, got %s", jsonStr, string(out)) + } + } + + type Foo struct { + P Priority `json:",omitempty"` + } + out, err := json.Marshal(new(Foo)) + if err != nil { + t.Fatal(err) + } + expected := "{}" + if string(out) != expected { + t.Fatal("expected omitempty to omit the flag") + } + for _, invalid := range []string{ + "0", "-1", "-2", "1.1", "0.0", + } { + var p Priority + err := json.Unmarshal([]byte(invalid), &p) + if err == nil { + t.Errorf("expected to fail to decode %s as a priority", invalid) + } + } +} + +func TestOptionalInteger(t *testing.T) { + makeInt64Pointer := func(v int64) *int64 { + return &v + } + + var defaultOptionalInt OptionalInteger + if !defaultOptionalInt.IsDefault() { + t.Fatal("should be the default") + } + if val := defaultOptionalInt.WithDefault(0); val != 0 { + t.Errorf("optional integer should have been 0, got %d", val) + } + + if val := defaultOptionalInt.WithDefault(1); val != 1 { + t.Errorf("optional integer should have been 1, got %d", val) + } + + if val := defaultOptionalInt.WithDefault(-1); val != -1 { + t.Errorf("optional integer should have been -1, got %d", val) + } + + var filledInt OptionalInteger + filledInt = OptionalInteger{value: makeInt64Pointer(1)} + if filledInt.IsDefault() { + t.Fatal("should not be the default") + } + if val := filledInt.WithDefault(0); val != 1 { + t.Errorf("optional integer should have been 1, got %d", val) + } + + if val := filledInt.WithDefault(-1); val != 1 { + t.Errorf("optional integer should have been 1, got %d", val) + } + + filledInt = OptionalInteger{value: makeInt64Pointer(0)} + if val := filledInt.WithDefault(1); val != 0 { + t.Errorf("optional integer should have been 0, got %d", val) + } + + for jsonStr, goValue := range map[string]OptionalInteger{ + "null": {}, + "0": {value: makeInt64Pointer(0)}, + "1": {value: makeInt64Pointer(1)}, + "-1": {value: makeInt64Pointer(-1)}, + } { + var d OptionalInteger + err := json.Unmarshal([]byte(jsonStr), &d) + if err != nil { + t.Fatal(err) + } + + if goValue.value == nil && d.value == nil { + } else if goValue.value == nil && d.value != nil { + t.Errorf("expected default, got %s", d) + } else if *d.value != *goValue.value { + t.Fatalf("expected %s, got %s", goValue, d) + } + + // Reverse + out, err := json.Marshal(goValue) + if err != nil { + t.Fatal(err) + } + if string(out) != jsonStr { + t.Fatalf("expected %s, got %s", jsonStr, string(out)) + } + } + + // marshal with omitempty + type Foo struct { + I *OptionalInteger `json:",omitempty"` + } + out, err := json.Marshal(new(Foo)) + if err != nil { + t.Fatal(err) + } + expected := "{}" + if string(out) != expected { + t.Fatal("expected omitempty to omit the optional integer") + } + + // unmarshal from omitempty output and get default value + var foo2 Foo + if err := json.Unmarshal(out, &foo2); err != nil { + t.Fatalf("%s failed to unmarshall with %s", string(out), err) + } + if i := foo2.I.WithDefault(42); i != 42 { + t.Fatalf("expected default value to be used, got %d", i) + } + if !foo2.I.IsDefault() { + t.Fatal("expected value to be the default") + } + + // test invalid values + for _, invalid := range []string{ + "foo", "-1.1", "1.1", "0.0", "[]", + } { + var p OptionalInteger + err := json.Unmarshal([]byte(invalid), &p) + if err == nil { + t.Errorf("expected to fail to decode %s as a priority", invalid) + } + } +} + +func TestOptionalString(t *testing.T) { + makeStringPointer := func(v string) *string { + return &v + } + + var defaultOptionalString OptionalString + if !defaultOptionalString.IsDefault() { + t.Fatal("should be the default") + } + if val := defaultOptionalString.WithDefault(""); val != "" { + t.Errorf("optional string should have been empty, got %s", val) + } + if val := defaultOptionalString.String(); val != "default" { + t.Fatalf("default optional string should be the 'default' string, got %s", val) + } + if val := defaultOptionalString.WithDefault("foo"); val != "foo" { + t.Errorf("optional string should have been foo, got %s", val) + } + + var filledStr OptionalString + filledStr = OptionalString{value: makeStringPointer("foo")} + if filledStr.IsDefault() { + t.Fatal("should not be the default") + } + if val := filledStr.WithDefault("bar"); val != "foo" { + t.Errorf("optional string should have been foo, got %s", val) + } + if val := filledStr.String(); val != "foo" { + t.Fatalf("optional string should have been foo, got %s", val) + } + filledStr = OptionalString{value: makeStringPointer("")} + if val := filledStr.WithDefault("foo"); val != "" { + t.Errorf("optional string should have been 0, got %s", val) + } + + for jsonStr, goValue := range map[string]OptionalString{ + "null": {}, + "\"0\"": {value: makeStringPointer("0")}, + "\"\"": {value: makeStringPointer("")}, + `"1"`: {value: makeStringPointer("1")}, + `"-1"`: {value: makeStringPointer("-1")}, + `"qwerty"`: {value: makeStringPointer("qwerty")}, + } { + var d OptionalString + err := json.Unmarshal([]byte(jsonStr), &d) + if err != nil { + t.Fatal(err) + } + + if goValue.value == nil && d.value == nil { + } else if goValue.value == nil && d.value != nil { + t.Errorf("expected default, got %s", d) + } else if *d.value != *goValue.value { + t.Fatalf("expected %s, got %s", goValue, d) + } + + // Reverse + out, err := json.Marshal(goValue) + if err != nil { + t.Fatal(err) + } + if string(out) != jsonStr { + t.Fatalf("expected %s, got %s", jsonStr, string(out)) + } + } + + // marshal with omitempty + type Foo struct { + S *OptionalString `json:",omitempty"` + } + out, err := json.Marshal(new(Foo)) + if err != nil { + t.Fatal(err) + } + expected := "{}" + if string(out) != expected { + t.Fatal("expected omitempty to omit the optional integer") + } + // unmarshal from omitempty output and get default value + var foo2 Foo + if err := json.Unmarshal(out, &foo2); err != nil { + t.Fatalf("%s failed to unmarshall with %s", string(out), err) + } + if s := foo2.S.WithDefault("foo"); s != "foo" { + t.Fatalf("expected default value to be used, got %s", s) + } + if !foo2.S.IsDefault() { + t.Fatal("expected value to be the default") + } + + for _, invalid := range []string{ + "[]", "{}", "0", "a", "'b'", + } { + var p OptionalString + err := json.Unmarshal([]byte(invalid), &p) + if err == nil { + t.Errorf("expected to fail to decode %s as an optional string", invalid) + } + } +} diff --git a/core/commands/bootstrap.go b/core/commands/bootstrap.go index 1aaa6456501..d572e8c079b 100644 --- a/core/commands/bootstrap.go +++ b/core/commands/bootstrap.go @@ -11,7 +11,7 @@ import ( fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" cmds "github.com/ipfs/go-ipfs-cmds" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" peer "github.com/libp2p/go-libp2p-core/peer" ma "github.com/multiformats/go-multiaddr" ) diff --git a/core/commands/cmdenv/env.go b/core/commands/cmdenv/env.go index c21612c3305..06d401db4cf 100644 --- a/core/commands/cmdenv/env.go +++ b/core/commands/cmdenv/env.go @@ -9,7 +9,7 @@ import ( "github.com/ipfs/go-ipfs/core" cmds "github.com/ipfs/go-ipfs-cmds" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" logging "github.com/ipfs/go-log" coreiface "github.com/ipfs/interface-go-ipfs-core" options "github.com/ipfs/interface-go-ipfs-core/options" diff --git a/core/commands/config.go b/core/commands/config.go index 86037ceb4f9..7a6e5abaf07 100644 --- a/core/commands/config.go +++ b/core/commands/config.go @@ -16,7 +16,7 @@ import ( "github.com/elgris/jsondiff" cmds "github.com/ipfs/go-ipfs-cmds" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" ) // ConfigUpdateOutput is config profile apply command's output diff --git a/core/commands/keystore.go b/core/commands/keystore.go index 5596ceee03a..4f7ca4af80f 100644 --- a/core/commands/keystore.go +++ b/core/commands/keystore.go @@ -14,9 +14,9 @@ import ( "text/tabwriter" cmds "github.com/ipfs/go-ipfs-cmds" - config "github.com/ipfs/go-ipfs-config" keystore "github.com/ipfs/go-ipfs-keystore" oldcmds "github.com/ipfs/go-ipfs/commands" + config "github.com/ipfs/go-ipfs/config" cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv" "github.com/ipfs/go-ipfs/core/commands/e" ke "github.com/ipfs/go-ipfs/core/commands/keyencode" diff --git a/core/commands/mount_unix.go b/core/commands/mount_unix.go index c6678d17585..1c72c6bd990 100644 --- a/core/commands/mount_unix.go +++ b/core/commands/mount_unix.go @@ -1,3 +1,4 @@ +//go:build !windows && !nofuse // +build !windows,!nofuse package commands @@ -10,7 +11,7 @@ import ( nodeMount "github.com/ipfs/go-ipfs/fuse/node" cmds "github.com/ipfs/go-ipfs-cmds" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" ) const ( diff --git a/core/commands/pin/remotepin.go b/core/commands/pin/remotepin.go index 0e4bf373ee0..495a1400367 100644 --- a/core/commands/pin/remotepin.go +++ b/core/commands/pin/remotepin.go @@ -17,7 +17,7 @@ import ( cid "github.com/ipfs/go-cid" cmds "github.com/ipfs/go-ipfs-cmds" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" "github.com/ipfs/go-ipfs/core/commands/cmdenv" fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" logging "github.com/ipfs/go-log" diff --git a/core/commands/swarm.go b/core/commands/swarm.go index 7c7ee3e814f..00899eacbed 100644 --- a/core/commands/swarm.go +++ b/core/commands/swarm.go @@ -16,7 +16,7 @@ import ( fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" cmds "github.com/ipfs/go-ipfs-cmds" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" inet "github.com/libp2p/go-libp2p-core/network" peer "github.com/libp2p/go-libp2p-core/peer" ma "github.com/multiformats/go-multiaddr" diff --git a/core/core_test.go b/core/core_test.go index 051b812c11a..e1563789e73 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -9,7 +9,7 @@ import ( datastore "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" ) func TestInitialization(t *testing.T) { diff --git a/core/coreapi/test/api_test.go b/core/coreapi/test/api_test.go index 54f3b48ca43..5c078558b7f 100644 --- a/core/coreapi/test/api_test.go +++ b/core/coreapi/test/api_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/ipfs/go-filestore" - "github.com/ipfs/go-ipfs-keystore" + keystore "github.com/ipfs/go-ipfs-keystore" "github.com/ipfs/go-ipfs/core" "github.com/ipfs/go-ipfs/core/bootstrap" "github.com/ipfs/go-ipfs/core/coreapi" @@ -19,12 +19,12 @@ import ( "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" - "github.com/ipfs/go-ipfs-config" + "github.com/ipfs/go-ipfs/config" coreiface "github.com/ipfs/interface-go-ipfs-core" "github.com/ipfs/interface-go-ipfs-core/tests" "github.com/libp2p/go-libp2p-core/crypto" peer "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p/p2p/net/mock" + mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" ) const testPeerID = "QmTFauExutTsy4XP6JbMFcw2Wa9645HJt2bTqL6qYDCKfe" diff --git a/core/corehttp/commands.go b/core/corehttp/commands.go index c5443f6eb3f..8de1e6be44a 100644 --- a/core/corehttp/commands.go +++ b/core/corehttp/commands.go @@ -16,7 +16,7 @@ import ( cmds "github.com/ipfs/go-ipfs-cmds" cmdsHttp "github.com/ipfs/go-ipfs-cmds/http" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" path "github.com/ipfs/go-path" ) diff --git a/core/corehttp/gateway_test.go b/core/corehttp/gateway_test.go index 8cccde0e22a..ae010421767 100644 --- a/core/corehttp/gateway_test.go +++ b/core/corehttp/gateway_test.go @@ -19,8 +19,8 @@ import ( datastore "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" - config "github.com/ipfs/go-ipfs-config" files "github.com/ipfs/go-ipfs-files" + config "github.com/ipfs/go-ipfs/config" path "github.com/ipfs/go-path" iface "github.com/ipfs/interface-go-ipfs-core" nsopts "github.com/ipfs/interface-go-ipfs-core/options/namesys" diff --git a/core/corehttp/hostname.go b/core/corehttp/hostname.go index 57c2c2191a6..6c0ad5bca13 100644 --- a/core/corehttp/hostname.go +++ b/core/corehttp/hostname.go @@ -18,7 +18,7 @@ import ( mbase "github.com/multiformats/go-multibase" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" iface "github.com/ipfs/interface-go-ipfs-core" options "github.com/ipfs/interface-go-ipfs-core/options" nsopts "github.com/ipfs/interface-go-ipfs-core/options/namesys" diff --git a/core/corehttp/hostname_test.go b/core/corehttp/hostname_test.go index f7ba89a8c9b..df0f4f22971 100644 --- a/core/corehttp/hostname_test.go +++ b/core/corehttp/hostname_test.go @@ -7,8 +7,8 @@ import ( "testing" cid "github.com/ipfs/go-cid" - config "github.com/ipfs/go-ipfs-config" files "github.com/ipfs/go-ipfs-files" + config "github.com/ipfs/go-ipfs/config" coreapi "github.com/ipfs/go-ipfs/core/coreapi" path "github.com/ipfs/go-path" ) diff --git a/core/coreunix/add_test.go b/core/coreunix/add_test.go index 7dd2b0cec65..de326559c3f 100644 --- a/core/coreunix/add_test.go +++ b/core/coreunix/add_test.go @@ -21,9 +21,9 @@ import ( "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" blockstore "github.com/ipfs/go-ipfs-blockstore" - config "github.com/ipfs/go-ipfs-config" files "github.com/ipfs/go-ipfs-files" pi "github.com/ipfs/go-ipfs-posinfo" + config "github.com/ipfs/go-ipfs/config" dag "github.com/ipfs/go-merkledag" coreiface "github.com/ipfs/interface-go-ipfs-core" ) diff --git a/core/mock/mock.go b/core/mock/mock.go index d0a5cd7f65c..65c028ac5e8 100644 --- a/core/mock/mock.go +++ b/core/mock/mock.go @@ -13,7 +13,7 @@ import ( "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p-core/host" diff --git a/core/node/bitswap.go b/core/node/bitswap.go index a2548ab3ce6..44698f92123 100644 --- a/core/node/bitswap.go +++ b/core/node/bitswap.go @@ -6,8 +6,8 @@ import ( "github.com/ipfs/go-bitswap" "github.com/ipfs/go-bitswap/network" blockstore "github.com/ipfs/go-ipfs-blockstore" - config "github.com/ipfs/go-ipfs-config" exchange "github.com/ipfs/go-ipfs-exchange-interface" + config "github.com/ipfs/go-ipfs/config" "github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/routing" "go.uber.org/fx" diff --git a/core/node/builder.go b/core/node/builder.go index 803caf51885..689f151b17a 100644 --- a/core/node/builder.go +++ b/core/node/builder.go @@ -14,7 +14,7 @@ import ( ds "github.com/ipfs/go-datastore" dsync "github.com/ipfs/go-datastore/sync" - cfg "github.com/ipfs/go-ipfs-config" + cfg "github.com/ipfs/go-ipfs/config" "github.com/libp2p/go-libp2p-core/crypto" peer "github.com/libp2p/go-libp2p-core/peer" ) diff --git a/core/node/dns.go b/core/node/dns.go index aeec23de145..2fc6327635c 100644 --- a/core/node/dns.go +++ b/core/node/dns.go @@ -6,7 +6,7 @@ import ( "strings" "time" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" doh "github.com/libp2p/go-doh-resolver" madns "github.com/multiformats/go-multiaddr-dns" diff --git a/core/node/groups.go b/core/node/groups.go index 80367156e55..26b2fae8475 100644 --- a/core/node/groups.go +++ b/core/node/groups.go @@ -7,8 +7,8 @@ import ( "time" blockstore "github.com/ipfs/go-ipfs-blockstore" - config "github.com/ipfs/go-ipfs-config" util "github.com/ipfs/go-ipfs-util" + config "github.com/ipfs/go-ipfs/config" "github.com/ipfs/go-log" "github.com/libp2p/go-libp2p-core/peer" pubsub "github.com/libp2p/go-libp2p-pubsub" diff --git a/core/node/libp2p/libp2p.go b/core/node/libp2p/libp2p.go index 309490bdc52..9d864ad467b 100644 --- a/core/node/libp2p/libp2p.go +++ b/core/node/libp2p/libp2p.go @@ -6,7 +6,7 @@ import ( "time" version "github.com/ipfs/go-ipfs" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" logging "github.com/ipfs/go-log" "github.com/libp2p/go-libp2p" diff --git a/core/node/libp2p/nat.go b/core/node/libp2p/nat.go index ce0ca345f70..28560662d2c 100644 --- a/core/node/libp2p/nat.go +++ b/core/node/libp2p/nat.go @@ -3,7 +3,7 @@ package libp2p import ( "time" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" "github.com/libp2p/go-libp2p" ) diff --git a/core/node/libp2p/relay.go b/core/node/libp2p/relay.go index 33d958d5c9d..d30adcfaed9 100644 --- a/core/node/libp2p/relay.go +++ b/core/node/libp2p/relay.go @@ -1,7 +1,7 @@ package libp2p import ( - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" "github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p" diff --git a/core/node/libp2p/sec.go b/core/node/libp2p/sec.go index bef2efe476a..6246d2fe30d 100644 --- a/core/node/libp2p/sec.go +++ b/core/node/libp2p/sec.go @@ -1,7 +1,7 @@ package libp2p import ( - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" "github.com/libp2p/go-libp2p" noise "github.com/libp2p/go-libp2p-noise" tls "github.com/libp2p/go-libp2p-tls" diff --git a/core/node/libp2p/smux.go b/core/node/libp2p/smux.go index 8ce540109ce..a405e5a3274 100644 --- a/core/node/libp2p/smux.go +++ b/core/node/libp2p/smux.go @@ -5,7 +5,7 @@ import ( "os" "strings" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" "github.com/libp2p/go-libp2p" smux "github.com/libp2p/go-libp2p-core/mux" mplex "github.com/libp2p/go-libp2p-mplex" diff --git a/core/node/libp2p/transport.go b/core/node/libp2p/transport.go index c5112e9c0b8..303a70d8061 100644 --- a/core/node/libp2p/transport.go +++ b/core/node/libp2p/transport.go @@ -3,7 +3,7 @@ package libp2p import ( "fmt" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" libp2p "github.com/libp2p/go-libp2p" metrics "github.com/libp2p/go-libp2p-core/metrics" libp2pquic "github.com/libp2p/go-libp2p-quic-transport" diff --git a/core/node/storage.go b/core/node/storage.go index 3e3e90fa174..6a647ffd7bc 100644 --- a/core/node/storage.go +++ b/core/node/storage.go @@ -3,7 +3,7 @@ package node import ( "github.com/ipfs/go-datastore" blockstore "github.com/ipfs/go-ipfs-blockstore" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" "go.uber.org/fx" "github.com/ipfs/go-filestore" diff --git a/go.mod b/go.mod index 8546b9057b3..af332571d8c 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/coreos/go-systemd/v22 v22.3.2 github.com/dustin/go-humanize v1.0.0 github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302 + github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 github.com/fsnotify/fsnotify v1.5.1 github.com/gabriel-vasile/mimetype v1.4.0 github.com/go-bindata/go-bindata/v3 v3.1.3 @@ -31,7 +32,6 @@ require ( github.com/ipfs/go-ipfs-blockstore v1.1.2 github.com/ipfs/go-ipfs-chunker v0.0.5 github.com/ipfs/go-ipfs-cmds v0.6.0 - github.com/ipfs/go-ipfs-config v0.19.0 github.com/ipfs/go-ipfs-exchange-interface v0.1.0 github.com/ipfs/go-ipfs-exchange-offline v0.1.1 github.com/ipfs/go-ipfs-files v0.0.9 diff --git a/go.sum b/go.sum index e791d5ea3cf..49abc3cac6d 100644 --- a/go.sum +++ b/go.sum @@ -476,8 +476,6 @@ github.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7Na github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8= github.com/ipfs/go-ipfs-cmds v0.6.0 h1:yAxdowQZzoFKjcLI08sXVNnqVj3jnABbf9smrPQmBsw= github.com/ipfs/go-ipfs-cmds v0.6.0/go.mod h1:ZgYiWVnCk43ChwoH8hAmI1IRbuVtq3GSTHwtRB/Kqhk= -github.com/ipfs/go-ipfs-config v0.19.0 h1:OuKIL+BkOZgJ+hb4Wg/9ynCtE/BaZBWcGy8hgdMepAo= -github.com/ipfs/go-ipfs-config v0.19.0/go.mod h1:wz2lKzOjgJeYJa6zx8W9VT7mz+iSd0laBMqS/9wmX6A= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= diff --git a/package-list.json b/package-list.json index 934e5e34515..7dfc5648cf0 100644 --- a/package-list.json +++ b/package-list.json @@ -42,7 +42,6 @@ ["ipfs/go-ipns", "go-ipns", "IPNS datastructures and validation logic"], "Repo", - ["ipfs/go-ipfs-config", "go-ipfs-config", "go-ipfs config file definitions"], ["ipfs/go-fs-lock", "go-fs-lock", "lockfile management functions"], ["ipfs/fs-repo-migrations", "fs-repo-migrations", "repo migrations"], diff --git a/plugin/loader/loader.go b/plugin/loader/loader.go index 167e1b3090b..6bf13a370c0 100644 --- a/plugin/loader/loader.go +++ b/plugin/loader/loader.go @@ -8,8 +8,8 @@ import ( "runtime" "strings" - config "github.com/ipfs/go-ipfs-config" - cserialize "github.com/ipfs/go-ipfs-config/serialize" + config "github.com/ipfs/go-ipfs/config" + cserialize "github.com/ipfs/go-ipfs/config/serialize" "github.com/ipld/go-ipld-prime/multicodec" "github.com/ipfs/go-ipfs/core" diff --git a/repo/fsrepo/config_test.go b/repo/fsrepo/config_test.go index f7c19c30765..0ffdababe27 100644 --- a/repo/fsrepo/config_test.go +++ b/repo/fsrepo/config_test.go @@ -10,7 +10,7 @@ import ( "github.com/ipfs/go-ipfs/plugin/loader" "github.com/ipfs/go-ipfs/repo/fsrepo" - "github.com/ipfs/go-ipfs-config" + "github.com/ipfs/go-ipfs/config" ) // note: to test sorting of the mountpoints in the disk spec they are diff --git a/repo/fsrepo/fsrepo.go b/repo/fsrepo/fsrepo.go index 2aa4a5e46c8..176ac46189b 100644 --- a/repo/fsrepo/fsrepo.go +++ b/repo/fsrepo/fsrepo.go @@ -20,9 +20,9 @@ import ( ds "github.com/ipfs/go-datastore" measure "github.com/ipfs/go-ds-measure" lockfile "github.com/ipfs/go-fs-lock" - config "github.com/ipfs/go-ipfs-config" - serialize "github.com/ipfs/go-ipfs-config/serialize" util "github.com/ipfs/go-ipfs-util" + config "github.com/ipfs/go-ipfs/config" + serialize "github.com/ipfs/go-ipfs/config/serialize" "github.com/ipfs/go-ipfs/repo/fsrepo/migrations" logging "github.com/ipfs/go-log" homedir "github.com/mitchellh/go-homedir" diff --git a/repo/fsrepo/fsrepo_test.go b/repo/fsrepo/fsrepo_test.go index cd2d66618a5..cf9aeabec0e 100644 --- a/repo/fsrepo/fsrepo_test.go +++ b/repo/fsrepo/fsrepo_test.go @@ -11,7 +11,7 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/assert" datastore "github.com/ipfs/go-datastore" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" ) // swap arg order diff --git a/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go b/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go index 11203ed5a05..21a6038a7eb 100644 --- a/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go +++ b/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go @@ -12,8 +12,8 @@ import ( "strings" "sync" - "github.com/ipfs/go-ipfs-config" files "github.com/ipfs/go-ipfs-files" + "github.com/ipfs/go-ipfs/config" "github.com/ipfs/go-ipfs/core" "github.com/ipfs/go-ipfs/core/coreapi" "github.com/ipfs/go-ipfs/core/node/libp2p" diff --git a/repo/fsrepo/migrations/migrations.go b/repo/fsrepo/migrations/migrations.go index 5eac91b2932..726870ea004 100644 --- a/repo/fsrepo/migrations/migrations.go +++ b/repo/fsrepo/migrations/migrations.go @@ -15,7 +15,7 @@ import ( "strings" "sync" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" ) const ( diff --git a/repo/fsrepo/migrations/migrations_test.go b/repo/fsrepo/migrations/migrations_test.go index 0e52b3a65ed..2472d4706aa 100644 --- a/repo/fsrepo/migrations/migrations_test.go +++ b/repo/fsrepo/migrations/migrations_test.go @@ -9,7 +9,7 @@ import ( "strings" "testing" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" ) func TestFindMigrations(t *testing.T) { diff --git a/repo/fsrepo/misc.go b/repo/fsrepo/misc.go index 7f0c01640a2..71b640331d4 100644 --- a/repo/fsrepo/misc.go +++ b/repo/fsrepo/misc.go @@ -3,7 +3,7 @@ package fsrepo import ( "os" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" homedir "github.com/mitchellh/go-homedir" ) diff --git a/repo/mock.go b/repo/mock.go index 0576498716a..15313ab373c 100644 --- a/repo/mock.go +++ b/repo/mock.go @@ -7,7 +7,7 @@ import ( filestore "github.com/ipfs/go-filestore" keystore "github.com/ipfs/go-ipfs-keystore" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" ma "github.com/multiformats/go-multiaddr" ) diff --git a/repo/repo.go b/repo/repo.go index 744bfdf5d57..00735eb94ee 100644 --- a/repo/repo.go +++ b/repo/repo.go @@ -9,7 +9,7 @@ import ( keystore "github.com/ipfs/go-ipfs-keystore" ds "github.com/ipfs/go-datastore" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" ma "github.com/multiformats/go-multiaddr" ) diff --git a/test/bench/bench_cli_ipfs_add/main.go b/test/bench/bench_cli_ipfs_add/main.go index 727a87aea2f..b11b5f83c7d 100644 --- a/test/bench/bench_cli_ipfs_add/main.go +++ b/test/bench/bench_cli_ipfs_add/main.go @@ -12,7 +12,7 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/unit" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" random "github.com/jbenet/go-random" ) diff --git a/test/bench/offline_add/main.go b/test/bench/offline_add/main.go index 94e8cac23ba..5d3f27fed1a 100644 --- a/test/bench/offline_add/main.go +++ b/test/bench/offline_add/main.go @@ -11,7 +11,7 @@ import ( "github.com/ipfs/go-ipfs/thirdparty/unit" - config "github.com/ipfs/go-ipfs-config" + config "github.com/ipfs/go-ipfs/config" random "github.com/jbenet/go-random" )