Skip to content
This repository was archived by the owner on Feb 7, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
reuse NewLocalShell, add DefautlShell
Try to reuse NewLocalShell to return a local Shell
add DefaultShell to return a shell from default api address
add test functions to NewLocalShell, DefaultShell and TryNewShell
  • Loading branch information
B YI committed Jul 28, 2018
commit ede2fd1075a1d3f757fe27b10448c084a8213c6f
127 changes: 40 additions & 87 deletions shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,38 +33,52 @@ const (
)

var (
defaultShellAddr string
defaultShellAddrLoadOnce sync.Once
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 @@ -92,92 +106,31 @@ func NewShellWithClient(url string, c *gohttp.Client) *Shell {
}
}

// Load all shell urls with error checking
func NewShellWithCheck(urls ...string) (*Shell, error) {
// 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, nil
return sh
}
}
}
return nil, errors.New(fmt.Sprintf("No working server in %v", urls))
}

func NewShellFromDefaultGateway() (*Shell, error) {
return NewShellWithCheck(DefaultGateway)
}

// Get all shell urls from api files
func newShellFromAPIFiles(files ...string) (urls []string) {
for _, apifile := range files {
if data, err := ioutil.ReadFile(apifile); err == nil {
url := strings.Trim(string(data), "\n\t ")
urls = append(urls, url)
}
}
return
}

func NewShellFromAPIFiles(files ...string) (*Shell, error) {
return NewShellWithCheck(newShellFromAPIFiles(files...)...)
}

// Get api url from ~/.ipfs/api
func newShellFromDefaultAPIFile() (urls []string) {
apifile, err := homedir.Expand(path.Join(DefaultPathRoot, DefaultApiFile))
if err != nil {
return urls
}
return newShellFromAPIFiles(apifile)
}

func NewShellFromDefaultAPIFile() (*Shell, error) {
return NewShellWithCheck(newShellFromDefaultAPIFile()...)
}

// get all shell urls from environmental variable IPFS_API and IPFS_PATH
func newShellFromEnv() (urls []string) {
if ipfsAPI := os.Getenv("IPFS_API"); ipfsAPI != "" {
urls = append(urls, ipfsAPI)
}
if ipfsPath := os.Getenv("IPFS_PATH"); ipfsPath != "" {
apifile := path.Join(ipfsPath, DefaultApiFile)
urls = append(urls, newShellFromAPIFiles(apifile)...)
}
return
}

func NewShellFromEnv() (*Shell, error) {
return NewShellWithCheck(newShellFromEnv()...)
return nil
}

// Load working default shell address once
func defaultShell() {
fmt.Println("ran defaultShell")
var urls []string
urls = append(urls, newShellFromEnv()...)
urls = append(urls, newShellFromDefaultAPIFile()...)
urls = append(urls, DefaultAPIAddr, DefaultGateway)
if sh, err := NewShellWithCheck(urls...); err == nil {
defaultShellAddr = sh.url
}
func getDefaultShell() {
defaultShell = TryNewShell(DefaultAPIAddr)
}

// Get shell from the default api address, may return nil if it is not working.
func DefaultShell() *Shell {
// Load once and cache working default shell address
defaultShellAddrLoadOnce.Do(defaultShell)
return NewShell(defaultShellAddr)
}

func DefaultShellWithCheck() (*Shell, error) {
// Load once and cache working default shell address
defaultShellAddrLoadOnce.Do(defaultShell)
return NewShellWithCheck(defaultShellAddr)
defaultShellLoadOnce.Do(getDefaultShell)
return defaultShell
}

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

Expand Down Expand Up @@ -242,3 +243,39 @@ func TestDagPut(t *testing.T) {
is.Nil(err)
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)
}