From c95b350f93fa926fd42be2351c252443128190d9 Mon Sep 17 00:00:00 2001 From: Jordan Rash <15827604+jordan-rash@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:39:20 -0600 Subject: [PATCH 1/9] stage one of update --- nex/nex.go | 32 ++++++++---- nex/update.go | 50 ------------------ nex/upgrade.go | 122 ++++++++++++++++++++++++++++++++++++++++++++ nex/upgrade_test.go | 54 ++++++++++++++++++++ 4 files changed, 198 insertions(+), 60 deletions(-) delete mode 100644 nex/update.go create mode 100644 nex/upgrade.go create mode 100644 nex/upgrade_test.go diff --git a/nex/nex.go b/nex/nex.go index 39ccbdec..52f90352 100644 --- a/nex/nex.go +++ b/nex/nex.go @@ -21,6 +21,8 @@ var ( COMMIT = "" BUILDDATE = "" + updatable = "" + blue = color.New(color.FgBlue).SprintFunc() ncli = fisk.New("nex", fmt.Sprintf("%s\nNATS Execution Engine CLI Version %s\n", blue(Banner), VERSION)) @@ -30,15 +32,16 @@ var ( _ = ncli.HelpFlag.Short('h') _ = ncli.WithCheats().CheatCommand.Hidden() - tui = ncli.Command("tui", "Start the Nex TUI [BETA]").Alias("ui") - nodes = ncli.Command("node", "Interact with execution engine nodes") - run = ncli.Command("run", "Run a workload on a target node") - yeet = ncli.Command("devrun", "Run a workload locating reasonable defaults (developer mode)").Alias("yeet") - stop = ncli.Command("stop", "Stop a running workload") - logs = ncli.Command("logs", "Live monitor workload log emissions") - evts = ncli.Command("events", "Live monitor events from nex nodes") - rootfs = ncli.Command("rootfs", "Build custom rootfs").Alias("fs") - lame = ncli.Command("lameduck", "Command a node to enter lame duck mdoe") + tui = ncli.Command("tui", "Start the Nex TUI [BETA]").Alias("ui") + nodes = ncli.Command("node", "Interact with execution engine nodes").Alias("nodes") + run = ncli.Command("run", "Run a workload on a target node") + yeet = ncli.Command("devrun", "Run a workload locating reasonable defaults (developer mode)").Alias("yeet") + stop = ncli.Command("stop", "Stop a running workload") + logs = ncli.Command("logs", "Live monitor workload log emissions") + evts = ncli.Command("events", "Live monitor events from nex nodes") + rootfs = ncli.Command("rootfs", "Build custom rootfs").Alias("fs") + lame = ncli.Command("lameduck", "Command a node to enter lame duck mode") + upgrade = ncli.Command("upgrade", "Upgrade the NEX CLI to the latest version") nodesLs = nodes.Command("ls", "List nodes") nodesInfo = nodes.Command("info", "Get information for an engine node") @@ -62,7 +65,7 @@ var ( ) func init() { - _ = versionCheck() + updatable, _ = versionCheck() ncli.Flag("server", "NATS server urls").Short('s').Envar("NATS_URL").Default(nats.DefaultURL).StringVar(&Opts.Servers) ncli.Flag("user", "Username or Token").Envar("NATS_USER").PlaceHolder("USER").StringVar(&Opts.Username) @@ -236,5 +239,14 @@ func main() { if err != nil { logger.Error("failed to build rootfs", slog.Any("err", err)) } + case upgrade.FullCommand(): + if updatable != "" { + _, err := UpgradeNex(ctx, logger, updatable) + if err != nil { + logger.Error("failed to upgrade nex", slog.Any("err", err)) + } + } else { + logger.Info("no new version found") + } } } diff --git a/nex/update.go b/nex/update.go deleted file mode 100644 index 92cc25a5..00000000 --- a/nex/update.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "net/http" -) - -func versionCheck() error { - if VERSION == "development" { - return nil - } - - res, err := http.Get("https://api.github.com/repos/synadia-io/nex/releases/latest") - if err != nil { - return err - } - defer res.Body.Close() - - b, err := io.ReadAll(res.Body) - if err != nil { - return err - } - - payload := make(map[string]interface{}) - err = json.Unmarshal(b, &payload) - if err != nil { - return err - } - - latestTag, ok := payload["tag_name"].(string) - if !ok { - return errors.New("error parsing tag_name") - } - - if latestTag != VERSION { - fmt.Printf(`================================================================ -🎉 There is a newer version [v%s] of the NEX CLI available 🎉 -To update, run: - curl -sSf https://nex.synadia.com/install.sh | sh -================================================================ - -`, - latestTag) - } - - return nil -} diff --git a/nex/upgrade.go b/nex/upgrade.go new file mode 100644 index 00000000..11e67165 --- /dev/null +++ b/nex/upgrade.go @@ -0,0 +1,122 @@ +package main + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "log/slog" + "net/http" + "os" + "os/exec" + "path/filepath" + "runtime" +) + +func versionCheck() (string, error) { + if VERSION == "development" { + return "", nil + } + + res, err := http.Get("https://api.github.com/repos/synadia-io/nex/releases/latest") + if err != nil { + return "", err + } + defer res.Body.Close() + + b, err := io.ReadAll(res.Body) + if err != nil { + return "", err + } + + payload := make(map[string]interface{}) + err = json.Unmarshal(b, &payload) + if err != nil { + return "", err + } + + latestTag, ok := payload["tag_name"].(string) + if !ok { + return "", errors.New("error parsing tag_name") + } + + if latestTag != VERSION { + fmt.Printf(`================================================================ +🎉 There is a newer version [v%s] of the NEX CLI available 🎉 +To update, run: + nex update +================================================================ + +`, + latestTag) + } + + return latestTag, nil +} + +func UpgradeNex(ctx context.Context, logger *slog.Logger, newVersion string) (string, error) { + nexPath, err := exec.LookPath("nex") + if err != nil { + return "", err + } + + _os := runtime.GOOS + arch := runtime.GOARCH + + url := fmt.Sprintf("https://github.com/synadia-io/nex/releases/download/%s/nex_%s_%s_%s", newVersion, newVersion, _os, arch) + resp, err := http.Get(url) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("failed to download nex: %s", resp.Status) + } + + dir, err := os.MkdirTemp(os.TempDir(), "nex-upgrade-*") + if err != nil { + return "", err + } + defer os.RemoveAll(dir) + + nexBinary, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + nex, err := os.Create(filepath.Join(dir, "nex")) + if err != nil { + return "", err + } + + _, err = nex.Write(nexBinary) + if err != nil { + return "", err + } + + h := sha256.New() + if _, err := io.Copy(h, nex); err != nil { + return "", err + } + + shasum := hex.EncodeToString(h.Sum(nil)) + logger.Debug("New binary downloaded", slog.String("sha256", shasum)) + + err = os.Chmod(filepath.Join(dir, "nex"), 0775) + if err != nil { + return "", err + } + + err = os.Rename(filepath.Join(dir, "nex"), nexPath) + if err != nil { + return "", err + } + + logger.Info("nex upgrade complete!", slog.String("new_version", newVersion)) + + return shasum, nil +} diff --git a/nex/upgrade_test.go b/nex/upgrade_test.go new file mode 100644 index 00000000..9a487013 --- /dev/null +++ b/nex/upgrade_test.go @@ -0,0 +1,54 @@ +package main + +import ( + "context" + "log/slog" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + shandler "github.com/jordan-rash/slog-handler" +) + +func logger(t *testing.T) *slog.Logger { + t.Helper() + return slog.New(shandler.NewHandler()) +} + +func setEnvironment(t *testing.T) { + t.Helper() + + tempPath := t.TempDir() + _, err := os.Create(filepath.Join(tempPath, "nex")) + if err != nil { + t.Fail() + } + err = os.Chmod(filepath.Join(tempPath, "nex"), 0775) + if err != nil { + t.Fail() + } + + path := os.Getenv("PATH") + os.Setenv("PATH", tempPath+":"+path) +} + +func TestUpdateNex(t *testing.T) { + setEnvironment(t) + log := logger(t) + + textNexPath, _ := exec.LookPath("nex") + t.Log("nex path: " + textNexPath) + if !strings.HasPrefix(textNexPath, os.TempDir()) { + t.Log("bailing on update nex test so real env isnt affected") + t.SkipNow() + } + + shasum, err := UpgradeNex(context.Background(), log, "0.2.1") + if err != nil { + t.Error(err) + } + + t.Log(shasum) +} From 8b6af6e80b4766643c5e9d0e05ca6669603993ab Mon Sep 17 00:00:00 2001 From: Jordan Rash <15827604+jordan-rash@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:46:37 -0600 Subject: [PATCH 2/9] all the test --- .github/workflows/ltb.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ltb.yml b/.github/workflows/ltb.yml index 091b9df9..0a8483c3 100644 --- a/.github/workflows/ltb.yml +++ b/.github/workflows/ltb.yml @@ -78,7 +78,7 @@ jobs: - name: Run test suite working-directory: . - run: go test -v -race ./test + run: go test -v -race ./... build: timeout-minutes: 10 From 4e2e72955a0b96a16af7bd56297ee9da787a07f5 Mon Sep 17 00:00:00 2001 From: Jordan Rash <15827604+jordan-rash@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:53:45 -0600 Subject: [PATCH 3/9] remove tests on windows for now --- .github/workflows/ltb.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ltb.yml b/.github/workflows/ltb.yml index 0a8483c3..81d9937e 100644 --- a/.github/workflows/ltb.yml +++ b/.github/workflows/ltb.yml @@ -78,6 +78,7 @@ jobs: - name: Run test suite working-directory: . + if: runner.os != 'Windows' run: go test -v -race ./... build: From af1ee243dae91038201398eb5754ecc6e84ef72a Mon Sep 17 00:00:00 2001 From: Jordan Rash <15827604+jordan-rash@users.noreply.github.com> Date: Fri, 26 Apr 2024 11:05:05 -0600 Subject: [PATCH 4/9] grr --- .github/workflows/ltb.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ltb.yml b/.github/workflows/ltb.yml index 81d9937e..af8ce8a4 100644 --- a/.github/workflows/ltb.yml +++ b/.github/workflows/ltb.yml @@ -79,7 +79,9 @@ jobs: name: Run test suite working-directory: . if: runner.os != 'Windows' - run: go test -v -race ./... + run: | + mv spec _spec + go test -v -race ./... build: timeout-minutes: 10 From 9f12bcb190a5db17e97c9dd088724562ed1c6355 Mon Sep 17 00:00:00 2001 From: Jordan Rash <15827604+jordan-rash@users.noreply.github.com> Date: Fri, 26 Apr 2024 11:37:14 -0600 Subject: [PATCH 5/9] backup binary during upgrade --- nex/upgrade.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/nex/upgrade.go b/nex/upgrade.go index 11e67165..d88b3d21 100644 --- a/nex/upgrade.go +++ b/nex/upgrade.go @@ -63,6 +63,30 @@ func UpgradeNex(ctx context.Context, logger *slog.Logger, newVersion string) (st return "", err } + f, err := os.Open(nexPath) + if err != nil { + return "", err + } + + // copy binary backup + f_bak, err := os.Create(nexPath + ".bak") + if err != nil { + return "", err + } + defer f_bak.Close() + defer os.Remove(nexPath + ".bak") + _, err = io.Copy(f_bak, f) + if err != nil { + return "", err + } + + restoreBackup := func() { + logger.Info("Restoring backup binary") + if err := os.Rename(nexPath+".bak", nexPath); err != nil { + logger.Error("Failed to restore backup binary", slog.Any("err", err)) + } + } + _os := runtime.GOOS arch := runtime.GOARCH @@ -113,6 +137,7 @@ func UpgradeNex(ctx context.Context, logger *slog.Logger, newVersion string) (st err = os.Rename(filepath.Join(dir, "nex"), nexPath) if err != nil { + restoreBackup() return "", err } From 6144728d7864fa3877f2d2cccf118a20700c6615 Mon Sep 17 00:00:00 2001 From: Jordan Rash <15827604+jordan-rash@users.noreply.github.com> Date: Fri, 26 Apr 2024 11:44:52 -0600 Subject: [PATCH 6/9] add a verify --- nex/upgrade.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/nex/upgrade.go b/nex/upgrade.go index d88b3d21..7ea3bf9d 100644 --- a/nex/upgrade.go +++ b/nex/upgrade.go @@ -141,6 +141,14 @@ func UpgradeNex(ctx context.Context, logger *slog.Logger, newVersion string) (st return "", err } + // Verify binary is working + output, err := exec.Command("nex", "--version").CombinedOutput() + if err != nil { + restoreBackup() + logger.Debug("Error running nex --version", slog.Any("err", err), slog.String("output", string(output))) + return "", err + } + logger.Info("nex upgrade complete!", slog.String("new_version", newVersion)) return shasum, nil From da007e323f74f45e4b27032aa61c9fb78a579931 Mon Sep 17 00:00:00 2001 From: Jordan Rash <15827604+jordan-rash@users.noreply.github.com> Date: Fri, 26 Apr 2024 12:51:18 -0600 Subject: [PATCH 7/9] remove verify --- nex/upgrade.go | 11 ++--------- nex/upgrade_test.go | 12 +++++++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/nex/upgrade.go b/nex/upgrade.go index 7ea3bf9d..d2f0268e 100644 --- a/nex/upgrade.go +++ b/nex/upgrade.go @@ -73,12 +73,13 @@ func UpgradeNex(ctx context.Context, logger *slog.Logger, newVersion string) (st if err != nil { return "", err } - defer f_bak.Close() defer os.Remove(nexPath + ".bak") _, err = io.Copy(f_bak, f) if err != nil { return "", err } + f_bak.Close() + f.Close() restoreBackup := func() { logger.Info("Restoring backup binary") @@ -141,14 +142,6 @@ func UpgradeNex(ctx context.Context, logger *slog.Logger, newVersion string) (st return "", err } - // Verify binary is working - output, err := exec.Command("nex", "--version").CombinedOutput() - if err != nil { - restoreBackup() - logger.Debug("Error running nex --version", slog.Any("err", err), slog.String("output", string(output))) - return "", err - } - logger.Info("nex upgrade complete!", slog.String("new_version", newVersion)) return shasum, nil diff --git a/nex/upgrade_test.go b/nex/upgrade_test.go index 9a487013..d09144c7 100644 --- a/nex/upgrade_test.go +++ b/nex/upgrade_test.go @@ -21,10 +21,12 @@ func setEnvironment(t *testing.T) { t.Helper() tempPath := t.TempDir() - _, err := os.Create(filepath.Join(tempPath, "nex")) + f, err := os.Create(filepath.Join(tempPath, "nex")) if err != nil { t.Fail() } + f.Close() + err = os.Chmod(filepath.Join(tempPath, "nex"), 0775) if err != nil { t.Fail() @@ -38,16 +40,16 @@ func TestUpdateNex(t *testing.T) { setEnvironment(t) log := logger(t) - textNexPath, _ := exec.LookPath("nex") - t.Log("nex path: " + textNexPath) - if !strings.HasPrefix(textNexPath, os.TempDir()) { + testNexPath, _ := exec.LookPath("nex") + t.Log("nex path: " + testNexPath) + if !strings.HasPrefix(testNexPath, os.TempDir()) { t.Log("bailing on update nex test so real env isnt affected") t.SkipNow() } shasum, err := UpgradeNex(context.Background(), log, "0.2.1") if err != nil { - t.Error(err) + t.Fatal(err) } t.Log(shasum) From fc43946d935cfca1286cbc31917b46a2c23476bf Mon Sep 17 00:00:00 2001 From: Jordan Rash <15827604+jordan-rash@users.noreply.github.com> Date: Fri, 26 Apr 2024 13:31:50 -0600 Subject: [PATCH 8/9] remove os.rename --- nex/upgrade.go | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/nex/upgrade.go b/nex/upgrade.go index d2f0268e..4ae5a6df 100644 --- a/nex/upgrade.go +++ b/nex/upgrade.go @@ -11,7 +11,6 @@ import ( "log/slog" "net/http" "os" - "os/exec" "path/filepath" "runtime" ) @@ -58,7 +57,7 @@ To update, run: } func UpgradeNex(ctx context.Context, logger *slog.Logger, newVersion string) (string, error) { - nexPath, err := exec.LookPath("nex") + nexPath, err := os.Executable() if err != nil { return "", err } @@ -102,24 +101,19 @@ func UpgradeNex(ctx context.Context, logger *slog.Logger, newVersion string) (st return "", fmt.Errorf("failed to download nex: %s", resp.Status) } - dir, err := os.MkdirTemp(os.TempDir(), "nex-upgrade-*") - if err != nil { - return "", err - } - defer os.RemoveAll(dir) - nexBinary, err := io.ReadAll(resp.Body) if err != nil { return "", err } - nex, err := os.Create(filepath.Join(dir, "nex")) + nex, err := os.Create(filepath.Join(filepath.Dir(nexPath), "nex")) if err != nil { return "", err } _, err = nex.Write(nexBinary) if err != nil { + restoreBackup() return "", err } @@ -129,19 +123,8 @@ func UpgradeNex(ctx context.Context, logger *slog.Logger, newVersion string) (st } shasum := hex.EncodeToString(h.Sum(nil)) - logger.Debug("New binary downloaded", slog.String("sha256", shasum)) - - err = os.Chmod(filepath.Join(dir, "nex"), 0775) - if err != nil { - return "", err - } - - err = os.Rename(filepath.Join(dir, "nex"), nexPath) - if err != nil { - restoreBackup() - return "", err - } + logger.Debug("New binary downloaded", slog.String("sha256", shasum)) logger.Info("nex upgrade complete!", slog.String("new_version", newVersion)) return shasum, nil From dcc319084bcff4876c354b14561d0cea40e2ea80 Mon Sep 17 00:00:00 2001 From: Jordan Rash <15827604+jordan-rash@users.noreply.github.com> Date: Fri, 26 Apr 2024 13:34:33 -0600 Subject: [PATCH 9/9] extra restore --- nex/upgrade.go | 1 + 1 file changed, 1 insertion(+) diff --git a/nex/upgrade.go b/nex/upgrade.go index 4ae5a6df..9c4a86fc 100644 --- a/nex/upgrade.go +++ b/nex/upgrade.go @@ -108,6 +108,7 @@ func UpgradeNex(ctx context.Context, logger *slog.Logger, newVersion string) (st nex, err := os.Create(filepath.Join(filepath.Dir(nexPath), "nex")) if err != nil { + restoreBackup() return "", err }