mirror of
https://github.com/ipfs/kubo.git
synced 2025-06-05 07:29:02 +08:00
make publish more configurable and add test for repub
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
This commit is contained in:
@ -173,10 +173,13 @@ func (i *cmdInvocation) Run(ctx context.Context) (output io.Reader, err error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if debug || u.GetenvBool("DEBUG") || os.Getenv("IPFS_LOGGING") == "debug" {
|
||||
if debug || os.Getenv("IPFS_LOGGING") == "debug" {
|
||||
u.Debug = true
|
||||
logging.SetDebugLogging()
|
||||
}
|
||||
if u.GetenvBool("DEBUG") {
|
||||
u.Debug = true
|
||||
}
|
||||
|
||||
res, err := callCommand(ctx, i.req, Root, i.cmd)
|
||||
if err != nil {
|
||||
@ -668,6 +671,6 @@ func apiClientForAddr(addr ma.Multiaddr) (cmdsHttp.Client, error) {
|
||||
}
|
||||
|
||||
func isConnRefused(err error) bool {
|
||||
return strings.Contains(err.Error(), "connection refused") ||
|
||||
strings.Contains(err.Error(), "target machine actively refused it")
|
||||
return strings.Contains(err.Error(), "connection refused") ||
|
||||
strings.Contains(err.Error(), "target machine actively refused it")
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
|
||||
|
||||
@ -46,8 +47,11 @@ Publish an <ipfs-path> to another public key (not implemented):
|
||||
},
|
||||
|
||||
Arguments: []cmds.Argument{
|
||||
cmds.StringArg("name", false, false, "The IPNS name to publish to. Defaults to your node's peerID"),
|
||||
cmds.StringArg("ipfs-path", true, false, "IPFS path of the obejct to be published at <name>").EnableStdin(),
|
||||
cmds.StringArg("ipfs-path", true, false, "IPFS path of the obejct to be published").EnableStdin(),
|
||||
},
|
||||
Options: []cmds.Option{
|
||||
cmds.BoolOption("resolve", "resolve given path before publishing (default=true)"),
|
||||
cmds.StringOption("lifetime", "t", "time duration that the record will be valid for (default: 24hrs)"),
|
||||
},
|
||||
Run: func(req cmds.Request, res cmds.Response) {
|
||||
log.Debug("Begin Publish")
|
||||
@ -65,32 +69,34 @@ Publish an <ipfs-path> to another public key (not implemented):
|
||||
}
|
||||
}
|
||||
|
||||
args := req.Arguments()
|
||||
pstr := req.Arguments()[0]
|
||||
|
||||
if n.Identity == "" {
|
||||
res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
var name string
|
||||
var pstr string
|
||||
|
||||
switch len(args) {
|
||||
case 2:
|
||||
name = args[0]
|
||||
pstr = args[1]
|
||||
if name != n.Identity.Pretty() {
|
||||
res.SetError(errors.New("keychains not yet implemented"), cmds.ErrNormal)
|
||||
return
|
||||
}
|
||||
case 1:
|
||||
// name = n.Identity.Pretty()
|
||||
pstr = args[0]
|
||||
popts := &publishOpts{
|
||||
verifyExists: true,
|
||||
pubValidTime: time.Hour * 24,
|
||||
}
|
||||
|
||||
// TODO n.Keychain.Get(name).PrivKey
|
||||
// TODO(cryptix): is req.Context().Context a child of n.Context()?
|
||||
output, err := publish(req.Context(), n, n.PrivateKey, path.Path(pstr))
|
||||
verif, found, _ := req.Option("resolve").Bool()
|
||||
if found {
|
||||
popts.verifyExists = verif
|
||||
}
|
||||
validtime, found, _ := req.Option("lifetime").String()
|
||||
if found {
|
||||
d, err := time.ParseDuration(validtime)
|
||||
if err != nil {
|
||||
res.SetError(fmt.Errorf("error parsing lifetime option: %s", err), cmds.ErrNormal)
|
||||
return
|
||||
}
|
||||
|
||||
popts.pubValidTime = d
|
||||
}
|
||||
|
||||
output, err := publish(req.Context(), n, n.PrivateKey, path.Path(pstr), popts)
|
||||
if err != nil {
|
||||
res.SetError(err, cmds.ErrNormal)
|
||||
return
|
||||
@ -107,14 +113,23 @@ Publish an <ipfs-path> to another public key (not implemented):
|
||||
Type: IpnsEntry{},
|
||||
}
|
||||
|
||||
func publish(ctx context.Context, n *core.IpfsNode, k crypto.PrivKey, ref path.Path) (*IpnsEntry, error) {
|
||||
// First, verify the path exists
|
||||
_, err := core.Resolve(ctx, n, ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
type publishOpts struct {
|
||||
verifyExists bool
|
||||
pubValidTime time.Duration
|
||||
}
|
||||
|
||||
func publish(ctx context.Context, n *core.IpfsNode, k crypto.PrivKey, ref path.Path, opts *publishOpts) (*IpnsEntry, error) {
|
||||
|
||||
if opts.verifyExists {
|
||||
// verify the path exists
|
||||
_, err := core.Resolve(ctx, n, ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = n.Namesys.Publish(ctx, k, ref)
|
||||
eol := time.Now().Add(opts.pubValidTime)
|
||||
err := n.Namesys.PublishWithEOL(ctx, k, ref, eol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
26
core/core.go
26
core/core.go
@ -56,6 +56,7 @@ import (
|
||||
pin "github.com/ipfs/go-ipfs/pin"
|
||||
repo "github.com/ipfs/go-ipfs/repo"
|
||||
config "github.com/ipfs/go-ipfs/repo/config"
|
||||
u "github.com/ipfs/go-ipfs/util"
|
||||
)
|
||||
|
||||
const IpnsValidatorTag = "ipns"
|
||||
@ -229,26 +230,45 @@ func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost
|
||||
n.Namesys = namesys.NewNameSystem(n.Routing)
|
||||
|
||||
// setup ipns republishing
|
||||
n.IpnsRepub = ipnsrp.NewRepublisher(n.Routing, n.Repo.Datastore(), n.Peerstore)
|
||||
n.IpnsRepub.AddName(n.Identity)
|
||||
err = n.setupIpnsRepublisher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *IpfsNode) setupIpnsRepublisher() error {
|
||||
cfg, err := n.Repo.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.IpnsRepub = ipnsrp.NewRepublisher(n.Routing, n.Repo.Datastore(), n.Peerstore)
|
||||
n.IpnsRepub.AddName(n.Identity)
|
||||
|
||||
if cfg.Ipns.RepublishPeriod != "" {
|
||||
d, err := time.ParseDuration(cfg.Ipns.RepublishPeriod)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failure to parse config setting IPNS.RepublishPeriod: %s", err)
|
||||
}
|
||||
|
||||
if d < time.Minute || d > (time.Hour*24) {
|
||||
if !u.Debug && (d < time.Minute || d > (time.Hour*24)) {
|
||||
return fmt.Errorf("config setting IPNS.RepublishPeriod is not between 1min and 1day: %s", d)
|
||||
}
|
||||
|
||||
n.IpnsRepub.Interval = d
|
||||
}
|
||||
|
||||
if cfg.Ipns.RecordLifetime != "" {
|
||||
d, err := time.ParseDuration(cfg.Ipns.RepublishPeriod)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failure to parse config setting IPNS.RecordLifetime: %s", err)
|
||||
}
|
||||
|
||||
n.IpnsRepub.RecordLifetime = d
|
||||
}
|
||||
|
||||
n.Process().Go(n.IpnsRepub.Run)
|
||||
|
||||
return nil
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
@ -37,6 +38,10 @@ func (m mockNamesys) Publish(ctx context.Context, name ci.PrivKey, value path.Pa
|
||||
return errors.New("not implemented for mockNamesys")
|
||||
}
|
||||
|
||||
func (m mockNamesys) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, _ time.Time) error {
|
||||
return errors.New("not implemented for mockNamesys")
|
||||
}
|
||||
|
||||
func newNodeWithMockNamesys(ns mockNamesys) (*core.IpfsNode, error) {
|
||||
c := config.Config{
|
||||
Identity: config.Identity{
|
||||
|
@ -31,6 +31,7 @@ package namesys
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
|
||||
ci "github.com/ipfs/go-ipfs/p2p/crypto"
|
||||
@ -105,4 +106,8 @@ type Publisher interface {
|
||||
// Publish establishes a name-value mapping.
|
||||
// TODO make this not PrivKey specific.
|
||||
Publish(ctx context.Context, name ci.PrivKey, value path.Path) error
|
||||
|
||||
// TODO: to be replaced by a more generic 'PublishWithValidity' type
|
||||
// call once the records spec is implemented
|
||||
PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package namesys
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
|
||||
ci "github.com/ipfs/go-ipfs/p2p/crypto"
|
||||
@ -81,3 +82,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error)
|
||||
func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error {
|
||||
return ns.publishers["/ipns/"].Publish(ctx, name, value)
|
||||
}
|
||||
|
||||
func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, val path.Path, eol time.Time) error {
|
||||
return ns.publishers["/ipns/"].PublishWithEOL(ctx, name, val, eol)
|
||||
}
|
||||
|
@ -2,4 +2,5 @@ package config
|
||||
|
||||
type Ipns struct {
|
||||
RepublishPeriod string
|
||||
RecordLifetime string
|
||||
}
|
||||
|
@ -56,13 +56,14 @@ test_expect_success "resolve output looks good" '
|
||||
|
||||
# publish with an explicit node ID
|
||||
|
||||
test_expect_success "'ipfs name publish <local-id> <hash>' succeeds" '
|
||||
test_expect_failure "'ipfs name publish <local-id> <hash>' succeeds" '
|
||||
PEERID=`ipfs id --format="<id>"` &&
|
||||
test_check_peerid "${PEERID}" &&
|
||||
echo ipfs name publish "${PEERID}" "/ipfs/$HASH_WELCOME_DOCS" &&
|
||||
ipfs name publish "${PEERID}" "/ipfs/$HASH_WELCOME_DOCS" >actual_node_id_publish
|
||||
'
|
||||
|
||||
test_expect_success "publish with our explicit node ID looks good" '
|
||||
test_expect_failure "publish with our explicit node ID looks good" '
|
||||
echo "Published to ${PEERID}: /ipfs/$HASH_WELCOME_DOCS" >expected_node_id_publish &&
|
||||
test_cmp expected_node_id_publish actual_node_id_publish
|
||||
'
|
||||
|
114
test/sharness/t0240-republisher.sh
Executable file
114
test/sharness/t0240-republisher.sh
Executable file
@ -0,0 +1,114 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2014 Jeromy Johnson
|
||||
# MIT Licensed; see the LICENSE file in this repository.
|
||||
#
|
||||
|
||||
test_description="Test ipfs repo operations"
|
||||
|
||||
. lib/test-lib.sh
|
||||
|
||||
export IPTB_ROOT="`pwd`/.iptb"
|
||||
export DEBUG=true
|
||||
|
||||
ipfsi() {
|
||||
local dir=$1; shift; IPFS_PATH="$IPTB_ROOT/$dir" ipfs $@
|
||||
}
|
||||
|
||||
setup_iptb() {
|
||||
test_expect_success "iptb init" '
|
||||
iptb init -n4 --bootstrap none --port 0
|
||||
'
|
||||
|
||||
test_expect_success "set configs up" '
|
||||
for i in `seq 0 3`
|
||||
do
|
||||
ipfsi $i config Ipns.RepublishPeriod 20s
|
||||
done
|
||||
'
|
||||
|
||||
test_expect_success "start up nodes" '
|
||||
iptb start
|
||||
'
|
||||
|
||||
test_expect_success "connect nodes" '
|
||||
iptb connect 0 1 &&
|
||||
iptb connect 0 2 &&
|
||||
iptb connect 0 3
|
||||
'
|
||||
|
||||
test_expect_success "nodes have connections" '
|
||||
ipfsi 0 swarm peers | grep ipfs &&
|
||||
ipfsi 1 swarm peers | grep ipfs &&
|
||||
ipfsi 2 swarm peers | grep ipfs &&
|
||||
ipfsi 3 swarm peers | grep ipfs
|
||||
'
|
||||
}
|
||||
|
||||
teardown_iptb() {
|
||||
test_expect_success "shut down nodes" '
|
||||
iptb kill
|
||||
'
|
||||
}
|
||||
|
||||
verify_can_resolve() {
|
||||
node=$1
|
||||
name=$2
|
||||
expected=$3
|
||||
|
||||
test_expect_success "node can resolve entry" '
|
||||
ipfsi $node name resolve $name > resolve
|
||||
'
|
||||
|
||||
test_expect_success "output looks right" '
|
||||
printf /ipfs/$expected > expected &&
|
||||
test_cmp resolve expected
|
||||
'
|
||||
}
|
||||
|
||||
verify_cannot_resolve() {
|
||||
node=$1
|
||||
name=$2
|
||||
|
||||
echo "verifying resolution fails on node $node"
|
||||
test_expect_success "node cannot resolve entry" '
|
||||
# TODO: this should work without the timeout option
|
||||
# but it currently hangs for some reason every so often
|
||||
test_expect_code 1 ipfsi $node name resolve --timeout=300ms $name
|
||||
'
|
||||
}
|
||||
|
||||
setup_iptb
|
||||
|
||||
test_expect_success "publish succeeds" '
|
||||
HASH=$(echo "foobar" | ipfsi 1 add -q) &&
|
||||
ipfsi 1 name publish -t 5s $HASH
|
||||
'
|
||||
|
||||
test_expect_success "other nodes can resolve" '
|
||||
id=$(ipfsi 1 id -f "<id>") &&
|
||||
verify_can_resolve 0 $id $HASH &&
|
||||
verify_can_resolve 1 $id $HASH &&
|
||||
verify_can_resolve 2 $id $HASH &&
|
||||
verify_can_resolve 3 $id $HASH
|
||||
'
|
||||
|
||||
test_expect_success "after five seconds, records are invalid" '
|
||||
go-sleep 5s &&
|
||||
verify_cannot_resolve 0 $id &&
|
||||
verify_cannot_resolve 1 $id &&
|
||||
verify_cannot_resolve 2 $id &&
|
||||
verify_cannot_resolve 3 $id
|
||||
'
|
||||
|
||||
test_expect_success "republisher fires after twenty seconds" '
|
||||
go-sleep 15s &&
|
||||
verify_can_resolve 0 $id $HASH &&
|
||||
verify_can_resolve 1 $id $HASH &&
|
||||
verify_can_resolve 2 $id $HASH &&
|
||||
verify_can_resolve 3 $id $HASH
|
||||
'
|
||||
|
||||
teardown_iptb
|
||||
|
||||
test_done
|
Reference in New Issue
Block a user