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

Kelp UI: Fix basepath and use pingURL to ping server in tailFile before redirect #404

Merged
merged 12 commits into from
Apr 16, 2020
Merged
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
27 changes: 25 additions & 2 deletions cmd/server_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/stellar/kelp/support/networking"
"github.com/stellar/kelp/support/prefs"
"github.com/stellar/kelp/support/sdk"
"github.com/stellar/kelp/support/utils"
)

const kelpPrefsDirectory = ".kelp"
Expand Down Expand Up @@ -89,6 +90,17 @@ func init() {
isLocalMode := env == envDev
isLocalDevMode := isLocalMode && *options.dev
kos := kelpos.GetKelpOS()
if isLocalMode {
wd, e := os.Getwd()
if e != nil {
panic(errors.Wrap(e, "could not get working directory"))
}
if filepath.Base(wd) != "kelp" {
e := fmt.Errorf("need to invoke from the root 'kelp' directory")
utils.PrintErrorHintf(e.Error())
panic(e)
}
}

var logFilepath *kelpos.OSPath
if !isLocalDevMode {
Expand Down Expand Up @@ -517,7 +529,7 @@ func openElectron(trayIconPath *kelpos.OSPath, url string) {
AppIconDefaultPath: "resources/[email protected]",
AcceptTCPTimeout: time.Minute * 2,
},
Debug: false,
Debug: true,
Windows: []*bootstrap.Window{&bootstrap.Window{
Homepage: url,
Options: &astilectron.WindowOptions{
Expand Down Expand Up @@ -697,10 +709,21 @@ const tailFileHTML = `<!-- taken from http://www.davejennifer.com/computerjunk/j
if (ajax.status == 200 || ajax.status == 206) {
if (ajax.responseText.includes("READY_STRING")) {
var redirectURL = "REDIRECT_URL";
var pingURL = "PING_URL";
document.getElementById("theEnd").innerHTML = "<br/><br/><b>redirecting to " + redirectURL + " ...</b><br/><br/>";
document.getElementById("theEnd").scrollIntoView();

// sleep for 2 seconds so the user sees that we are being redirected
setTimeout(() => { window.location.href = redirectURL; }, 2000)
setTimeout(() => {
var ajaxPing = new XMLHttpRequest();
ajaxPing.open("GET", pingURL, true);
ajaxPing.onreadystatechange = function () {
if ((ajaxPing.readyState == 4) && (ajaxPing.status == 200)) {
window.location.href = redirectURL;
}
}
ajaxPing.send(null);
}, 2000)
}
}
}// ready state 4
Expand Down
21 changes: 16 additions & 5 deletions gui/backend/api_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"log"
"net/http"
"os"
"path/filepath"

"github.com/stellar/go/clients/horizonclient"
"github.com/stellar/kelp/support/kelpos"
Expand All @@ -15,7 +16,7 @@ import (
// APIServer is an instance of the API service
type APIServer struct {
basepath *kelpos.OSPath
kelpBinPath *kelpos.OSPath
kelpBinName string
configsDir *kelpos.OSPath
logsDir *kelpos.OSPath
kos *kelpos.KelpOS
Expand All @@ -42,7 +43,7 @@ func MakeAPIServer(
noHeaders bool,
quitFn func(),
) (*APIServer, error) {
kelpBinPath := basepath.Join(os.Args[0])
kelpBinName := filepath.Base(os.Args[0])
configsDir := basepath.Join("ops", "configs")
logsDir := basepath.Join("ops", "logs")

Expand All @@ -53,7 +54,7 @@ func MakeAPIServer(

return &APIServer{
basepath: basepath,
kelpBinPath: kelpBinPath,
kelpBinName: kelpBinName,
configsDir: configsDir,
logsDir: logsDir,
kos: kos,
Expand Down Expand Up @@ -119,12 +120,22 @@ func (s *APIServer) writeJsonWithLog(w http.ResponseWriter, v interface{}, doLog
}

func (s *APIServer) runKelpCommandBlocking(namespace string, cmd string) ([]byte, error) {
cmdString := fmt.Sprintf("%s %s", s.kelpBinPath.Unix(), cmd)
// There is a weird issue on windows where the absolute path for the kelp binary does not work on the release GUI
// version because of the unzipped directory name but it will work on the released cli version or if we change the
// name of the folder in which the GUI version is unzipped.
// To avoid these issues we only invoke with the binary name as opposed to the absolute path that contains the
// directory name. see start_bot.go for some experimentation with absolute and relative paths
cmdString := fmt.Sprintf("./%s %s", s.kelpBinName, cmd)
return s.kos.Blocking(namespace, cmdString)
}

func (s *APIServer) runKelpCommandBackground(namespace string, cmd string) (*kelpos.Process, error) {
cmdString := fmt.Sprintf("%s %s", s.kelpBinPath.Unix(), cmd)
// There is a weird issue on windows where the absolute path for the kelp binary does not work on the release GUI
// version because of the unzipped directory name but it will work on the released cli version or if we change the
// name of the folder in which the GUI version is unzipped.
// To avoid these issues we only invoke with the binary name as opposed to the absolute path that contains the
// directory name. see start_bot.go for some experimentation with absolute and relative paths
cmdString := fmt.Sprintf("./%s %s", s.kelpBinName, cmd)
return s.kos.Background(namespace, cmdString)
}

Expand Down
3 changes: 3 additions & 0 deletions gui/backend/start_bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ func (s *APIServer) doStartBot(botName string, strategy string, iterations *uint
// - native absolute paths did work on windows!
// - unix absolute path did not work on windows
//
// (see api_server.go#runKelpCommandBlocking and #runKelpCommandBackground for information that may be related to why
// absolute paths did not work)
//
// The above experimentation makes unix relative paths the most common format so we will use that to start new bots
//
// Note that on windows it could use the native windows naming scheme (C:\ etc.) but in the linux subsystem on windows
Expand Down
4 changes: 2 additions & 2 deletions scripts/fs_bin_gen/gui/filesystem_vfsdata_dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"path/filepath"
)

// build dir is ../gui/web/build because we run from the bin directory in dev mode
var guiBuildDir = filepath.Join("..", "gui", "web", "build")
// build dir is gui/web/build because we run from the root kelp directory in dev mode
var guiBuildDir = filepath.Join("gui", "web", "build")

// file system for GUI
var FS = http.Dir(guiBuildDir)
19 changes: 15 additions & 4 deletions support/kelpos/kelpos.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kelpos

import (
"fmt"
"io"
"os/exec"
"sync"
Expand All @@ -10,6 +11,7 @@ import (

// KelpOS is a struct that manages all subprocesses started by this Kelp process
type KelpOS struct {
workingBinDir *OSPath
processes map[string]Process
processLock *sync.Mutex
bots map[string]*BotInstance
Expand All @@ -33,11 +35,17 @@ type Process struct {
var singleton *KelpOS

func init() {
path, e := MakeOsPathBase()
if e != nil {
panic(e)
}

singleton = &KelpOS{
processes: map[string]Process{},
processLock: &sync.Mutex{},
bots: map[string]*BotInstance{},
botLock: &sync.Mutex{},
workingBinDir: path,
processes: map[string]Process{},
processLock: &sync.Mutex{},
bots: map[string]*BotInstance{},
botLock: &sync.Mutex{},
}
}

Expand All @@ -49,5 +57,8 @@ type BotInstance struct {

// GetKelpOS gets the singleton instance
func GetKelpOS() *KelpOS {
if singleton == nil {
panic(fmt.Errorf("there is a cycle stemming from the init() method since singleton was nil"))
}
return singleton
}
55 changes: 45 additions & 10 deletions support/kelpos/ospath.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"runtime/debug"
"strings"

"github.com/stellar/kelp/support/utils"
)

// OSPath encapsulates the pair of the native path (i.e. windows or unix) and the unix path
Expand Down Expand Up @@ -40,18 +43,24 @@ func makeOSPath(native string, unix string, isRel bool) *OSPath {

// MakeOsPathBase is a factory method for the OSPath struct based on the current binary's directory
func MakeOsPathBase() (*OSPath, error) {
currentDirUnslashed, e := getCurrentDirUnix()
if e != nil {
return nil, fmt.Errorf("could not get current directory: %s", e)
}
binaryDirectoryNative, e := getBinaryDirectoryNative()
if e != nil {
return nil, fmt.Errorf("could not get binary directory: %s", e)
}
ospath := makeOSPath(binaryDirectoryNative, currentDirUnslashed, false)

binaryDirectoryUnix, e := getBinaryDirectoryUnix(binaryDirectoryNative)
if e != nil {
return nil, fmt.Errorf("could not get binary directory unix: %s", e)
}

// use the binary directory for the base path since UI executables will be run from $PWD whereas the binary can be located
// in a different path (for example $PWD/Kelp.app/Contents/MacOS/ for darwin)
ospath := makeOSPath(binaryDirectoryNative, binaryDirectoryUnix, false)

if filepath.Base(ospath.Native()) != filepath.Base(ospath.Unix()) {
return nil, fmt.Errorf("ran from directory (%s) but need to run from the same directory as the location of the binary (%s), cd over to the location of the binary", ospath.Unix(), ospath.Native())
errorStr := fmt.Sprintf("ran from directory (%s) but need to run from the same directory as the location of the binary (%s), cd over to the location of the binary\n", ospath.Unix(), ospath.Native())
utils.PrintErrorHintf(errorStr)
return nil, fmt.Errorf(errorStr)
}

return ospath, nil
Expand Down Expand Up @@ -125,11 +134,37 @@ func (o *OSPath) RelFromPath(basepath *OSPath) (*OSPath, error) {
return makeOSPath(newRelNative, newRelUnix, true), nil
}

func getCurrentDirUnix() (string, error) {
kos := GetKelpOS()
outputBytes, e := kos.Blocking("pwd", "pwd")
// getBinaryDirectoryUnix takes the workingDirUnix and adds to it the relative path from
// workingDirNative to binaryDirectoryNative in unix form.
func getBinaryDirectoryUnix(binaryDirectoryNative string) (string, error) {
wdUnix, e := getWorkingDirUnix()
if e != nil {
return "", fmt.Errorf("could not fetch working directory unix: %s", e)
}

wdNative, e := os.Getwd()
if e != nil {
return "", fmt.Errorf("could not fetch working directory native: %s", e)
}

// on windows the native format is not unix-based so we need to convert from C:\ vs /mnt/c notation. This method will
// standardize conversions across OSes but results in a nop for darwin/unix since they use unix paths as their native format
return convertNativePathToUnix(wdNative, binaryDirectoryNative, wdUnix)
}

func convertNativePathToUnix(baseNative string, targetNative string, baseUnix string) (string, error) {
relBaseToTarget, e := filepath.Rel(baseNative, targetNative)
if e != nil {
return "", fmt.Errorf("could not fetch relative path from baseNative (%s) to targetNative (%s): %s", baseNative, targetNative, e)
}

return toUnixFilepath(filepath.Join(baseUnix, toUnixFilepath(relBaseToTarget))), nil
}

func getWorkingDirUnix() (string, error) {
outputBytes, e := exec.Command("bash", "-c", "pwd").Output()
if e != nil {
return "", fmt.Errorf("could not fetch current directory: %s", e)
return "", fmt.Errorf("could not run raw command 'bash -c pwd': %s", e)
}
return strings.TrimSpace(string(outputBytes)), nil
}
Expand Down
102 changes: 94 additions & 8 deletions support/kelpos/ospath_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,7 @@ func TestOSPath(t *testing.T) {
for _, k := range testCases {
t.Run(k.basePathNative, func(t *testing.T) {
// early exit if running on a disallowed platform to avoid false negatives
isValid := false
for _, allowedGoos := range k.runGoos {
if runtime.GOOS == allowedGoos {
isValid = true
break
}
}
if !isValid {
if !checkGoosAllowed(k.runGoos) {
return
}

Expand Down Expand Up @@ -80,3 +73,96 @@ func TestOSPath(t *testing.T) {
})
}
}

func TestConvertNativePathToUnix(t *testing.T) {
testCases := []struct {
name string
runGoos []string
baseNative string
targetNative string
baseUnix string
wantTargetUnix string
}{
{
name: "unix_forward",
runGoos: []string{"linux", "darwin"},
baseNative: "/Users/a/test",
targetNative: "/Users/a/test/b",
baseUnix: "/Users/a/test",
wantTargetUnix: "/Users/a/test/b",
}, {
name: "unix_forward_trailslash",
runGoos: []string{"linux", "darwin"},
baseNative: "/Users/a/test/",
targetNative: "/Users/a/test/b/",
baseUnix: "/Users/a/test/",
wantTargetUnix: "/Users/a/test/b",
}, {
name: "unix_backward",
runGoos: []string{"linux", "darwin"},
baseNative: "/Users/a/test",
targetNative: "/Users/a",
baseUnix: "/Users/a/test",
wantTargetUnix: "/Users/a",
}, {
name: "unix_backward_trailslash",
runGoos: []string{"linux", "darwin"},
baseNative: "/Users/a/test/",
targetNative: "/Users/a/",
baseUnix: "/Users/a/test/",
wantTargetUnix: "/Users/a",
}, {
name: "windows_forward",
runGoos: []string{"windows"},
baseNative: "C:\\Users\\a\\test",
targetNative: "C:\\Users\\a\\test\\b",
baseUnix: "/Users/a/test",
wantTargetUnix: "/Users/a/test/b",
}, {
name: "windows_forward_trailslash",
runGoos: []string{"windows"},
baseNative: "C:\\Users\\a\\test\\",
targetNative: "C:\\Users\\a\\test\\b\\",
baseUnix: "/Users/a/test/",
wantTargetUnix: "/Users/a/test/b",
}, {
name: "windows_backward",
runGoos: []string{"windows"},
baseNative: "C:\\Users\\a\\test",
targetNative: "C:\\Users\\a",
baseUnix: "/Users/a/test",
wantTargetUnix: "/Users/a",
}, {
name: "windows_backward_trailslash",
runGoos: []string{"windows"},
baseNative: "C:\\Users\\a\\test\\",
targetNative: "C:\\Users\\a\\",
baseUnix: "/Users/a/test/",
wantTargetUnix: "/Users/a",
},
}

for _, k := range testCases {
t.Run(k.name, func(t *testing.T) {
// early exit if running on a disallowed platform to avoid false negatives
if !checkGoosAllowed(k.runGoos) {
return
}

targetUnix, e := convertNativePathToUnix(k.baseNative, k.targetNative, k.baseUnix)
if !assert.NoError(t, e) {
return
}
assert.Equal(t, k.wantTargetUnix, targetUnix)
})
}
}

func checkGoosAllowed(runGoos []string) bool {
for _, allowedGoos := range runGoos {
if runtime.GOOS == allowedGoos {
return true
}
}
return false
}
Loading