-
Notifications
You must be signed in to change notification settings - Fork 178
Add DefaultShell as required by #69 #106
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ import ( | |
"os" | ||
"path" | ||
"strings" | ||
"sync" | ||
"time" | ||
|
||
files "github.com/ipfs/go-ipfs-cmdkit/files" | ||
|
@@ -27,6 +28,13 @@ const ( | |
DefaultPathRoot = "~/" + DefaultPathName | ||
DefaultApiFile = "api" | ||
EnvDir = "IPFS_PATH" | ||
DefaultGateway = "https://ipfs.io" | ||
DefaultAPIAddr = "/ip4/127.0.0.1/tcp/5001" | ||
) | ||
|
||
var ( | ||
defaultShellAddr string | ||
defaultShellAddrLoadOnce sync.Once | ||
) | ||
|
||
type Shell struct { | ||
|
@@ -84,6 +92,94 @@ func NewShellWithClient(url string, c *gohttp.Client) *Shell { | |
} | ||
} | ||
|
||
// Load all shell urls with error checking | ||
func NewShellWithCheck(urls ...string) (*Shell, error) { | ||
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 nil, errors.New(fmt.Sprintf("No working server in %v", urls)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd just return |
||
} | ||
|
||
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()...) | ||
} | ||
|
||
// 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 DefaultShell() *Shell { | ||
// Load once and cache working default shell address | ||
defaultShellAddrLoadOnce.Do(defaultShell) | ||
return NewShell(defaultShellAddr) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd just reuse the same shell. Its thread-safe and, really, we want to reuse the http client. Also, that'd get rid of the need for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the apifile does exist, but an error occurred while reading the apifile, should we just return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm. Yeah, that's unfortunate. We shouldn't panic. We could:
I'd prefer 2. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mind clarify where should I put the error? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put it in a variable, guarded by a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did not add a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to apologize for delays, there's no rush on this. |
||
} | ||
|
||
func DefaultShellWithCheck() (*Shell, error) { | ||
// Load once and cache working default shell address | ||
defaultShellAddrLoadOnce.Do(defaultShell) | ||
return NewShellWithCheck(defaultShellAddr) | ||
} | ||
|
||
func (s *Shell) SetTimeout(d time.Duration) { | ||
s.httpcli.Timeout = d | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe
TryNewShell
? Not happy with the name but can't think of a better one.Anyways, this should probably have some more documentation (the description doesn't explain what "error checking" means and would otherwise lead me to believe that it returns a slice of shells).