Skip to content
This repository has been archived by the owner on Feb 7, 2024. It is now read-only.

Add DefaultShell as required by #69 #106

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 63 additions & 14 deletions shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"os"
"path"
"strings"
"sync"
"time"

files "github.com/ipfs/go-ipfs-cmdkit/files"
Expand All @@ -29,36 +30,57 @@ const (
DefaultPathRoot = "~/" + DefaultPathName
DefaultApiFile = "api"
EnvDir = "IPFS_PATH"
DefaultGateway = "https://ipfs.io"
DefaultAPIAddr = "/ip4/127.0.0.1/tcp/5001"
)

var (
localShell *Shell
LocalShellError error // Reading apiFile may raise this error.
localShellLoadOnce sync.Once
defaultShell *Shell
defaultShellLoadOnce sync.Once
)

type Shell struct {
url string
httpcli *gohttp.Client
}

func NewLocalShell() *Shell {
func newLocalShell() {
ipfsAPI := os.Getenv("IPFS_API")
if ipfsAPI != "" {
localShell = NewShell(strings.TrimSpace(ipfsAPI))
return
}

baseDir := os.Getenv(EnvDir)
if baseDir == "" {
baseDir = DefaultPathRoot
}

baseDir, err := homedir.Expand(baseDir)
if err != nil {
return nil
}

apiFile := path.Join(baseDir, DefaultApiFile)

if _, err := os.Stat(apiFile); err != nil {
return nil
if apiFile, err := homedir.Expand(apiFile); err == nil {
if _, err := os.Stat(apiFile); err == nil {
api, err := ioutil.ReadFile(apiFile)
if err != nil {
LocalShellError = err
return
}
localShell = NewShell(strings.TrimSpace(string(api)))
return
}
}

api, err := ioutil.ReadFile(apiFile)
if err != nil {
return nil
}
localShell = NewShell(DefaultGateway)
}

return NewShell(strings.TrimSpace(string(api)))
// Try to obtain a new shell from the following sources, returns the first found one.
// The sources are $IPFS_API, api file under $IPFS_PATH or ~/.ipfs and the default gateway.
// Will ignore api file if it does not exist, but may rasie APIFileError if unable to read it.
func NewLocalShell() *Shell {
localShellLoadOnce.Do(newLocalShell)
return localShell
}

func NewShell(url string) *Shell {
Expand Down Expand Up @@ -86,6 +108,33 @@ func NewShellWithClient(url string, c *gohttp.Client) *Shell {
}
}

// Try to obtain a working shell from the urls given in the arguments.
// Returns the first working shell or returns nil when none of the urls works.
func TryNewShell(urls ...string) *Shell {
encountered := map[string]struct{}{}
for _, url := range urls {
if _, ok := encountered[url]; !ok {
encountered[url] = struct{}{}
sh := NewShell(url)
_, _, err := sh.Version()
if err == nil {
return sh
}
}
}
return nil
}

func getDefaultShell() {
defaultShell = TryNewShell(DefaultAPIAddr)
}

// Get shell from the default api address, may return nil if it is not working.
func DefaultShell() *Shell {
defaultShellLoadOnce.Do(getDefaultShell)
return defaultShell
}

func (s *Shell) SetTimeout(d time.Duration) {
s.httpcli.Timeout = d
}
Expand Down
35 changes: 35 additions & 0 deletions shell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"crypto/md5"
"fmt"
"io"
"os"
"testing"
"time"

Expand Down Expand Up @@ -244,6 +245,40 @@ func TestDagPut(t *testing.T) {
is.Equal(c, "zdpuAt47YjE9XTgSxUBkiYCbmnktKajQNheQBGASHj3FfYf8M")
}

func TestNewLocalShell(t *testing.T) {
is := is.New(t)
shell1 := NewLocalShell()
err := LocalShellError
is.Nil(err)
is.NotNil(shell1)
ipfs_api := "mySimpleTest"
os.Setenv("IPFS_API", ipfs_api)
shell2 := NewLocalShell()
is.Nil(err)
is.NotNil(shell2)
// NewLocalShell is guarded by a sync.Once
is.Equal(shell1, shell2)
// run newLocalShell one more time to make environmental variable take effects.
newLocalShell()
shell3 := NewLocalShell()
is.Equal(shell3.url, ipfs_api)
}

func TestDefaultShell(t *testing.T) {
is := is.New(t)
sh := DefaultShell()
is.NotNil(sh)
is.OK(sh.IsUp())
}

func TestTryNewShell(t *testing.T) {
is := is.New(t)
sh := TryNewShell(DefaultGateway, DefaultAPIAddr)
is.NotNil(sh)
is.Equal(sh.url, DefaultGateway)
is.OK(sh.IsUp())
sh = TryNewShell("some-nonexistent-URL")
is.Nil(sh)
func TestStatsBW(t *testing.T) {
is := is.New(t)
s := NewShell(shellUrl)
Expand Down