From ce46b848dd615711b7863387756c07a71e9b9e92 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Fri, 18 Feb 2022 09:16:20 +0100 Subject: [PATCH] Fix upgrade strings --- main.go | 57 +++++++++++++++++-- update/main.go | 127 ++++++++++++++++++++++++++++++++++++++++++ update/update_test.go | 36 ++++++++++++ 3 files changed, 216 insertions(+), 4 deletions(-) create mode 100644 update/main.go create mode 100644 update/update_test.go diff --git a/main.go b/main.go index e86db36..8a5ffbb 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,11 @@ package main import ( + "fmt" "katenary/cmd" "katenary/generator/writers" "katenary/helm" + "katenary/update" "strconv" "github.com/spf13/cobra" @@ -92,10 +94,57 @@ func main() { }, } - rootCmd.AddCommand(versionCmd) - rootCmd.AddCommand(convertCmd) - rootCmd.AddCommand(showLabelsCmd) + // Update the binary to the latest version + updateCmd := &cobra.Command{ + Use: "upgrade", + Short: "Upgrade katenary to the latest version if available", + Run: func(c *cobra.Command, args []string) { + version, assets, err := update.CheckLatestVersion() + if err != nil { + c.Println(err) + return + } + c.Println("Updating to version: " + version) + err = update.DownloadLatestVersion(assets) + if err != nil { + c.Println(err) + return + } + c.Println("Update completed") + }, + } - rootCmd.Execute() + rootCmd.AddCommand( + versionCmd, + convertCmd, + showLabelsCmd, + updateCmd, + ) + // in parallel, check if the current katenary version is the latest + ch := make(chan string) + go func() { + version, _, err := update.CheckLatestVersion() + if err != nil { + ch <- "" + return + } + if cmd.Version != version { + ch <- fmt.Sprintf("\x1b[33mNew version available: " + + version + + " - to auto upgrade katenary, you can execute: katenary upgrade\x1b[0m\n") + } + }() + + // Execute the command + finalize := make(chan error) + go func() { + finalize <- rootCmd.Execute() + }() + + // Wait for both goroutines to finish + if err := <-finalize; err != nil { + fmt.Println(err) + } + fmt.Print(<-ch) } diff --git a/update/main.go b/update/main.go new file mode 100644 index 0000000..ca090be --- /dev/null +++ b/update/main.go @@ -0,0 +1,127 @@ +package update + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "katenary/cmd" + "net/http" + "os" + "runtime" + "time" +) + +var exe, _ = os.Executable() + +// Asset is a github asset from release url. +type Asset struct { + Name string `json:"name"` + URL string `json:"browser_download_url"` +} + +// CheckLatestVersion check katenary latest version from release and propose to download it +func CheckLatestVersion() (string, []Asset, error) { + + githuburl := "https://api.github.com/repos/metal3d/katenary/releases/latest" + // Create a HTTP client with 1s timeout + client := &http.Client{ + Timeout: time.Second * 1, + } + // Create a request + req, err := http.NewRequest("GET", githuburl, nil) + if err != nil { + return "", nil, err + } + + // Send the request via a client + resp, err := client.Do(req) + if err != nil { + return "", nil, err + } + defer resp.Body.Close() + + // Get tag_name from the json response + var release = struct { + TagName string `json:"tag_name"` + Assets []Asset `json:"assets"` + }{} + err = json.NewDecoder(resp.Body).Decode(&release) + if err != nil { + return "", nil, err + } + + if release.TagName == "" { + return "", nil, errors.New("No release found") + } + + if cmd.Version == release.TagName { + fmt.Println("You are using the latest version") + return "", nil, errors.New("You are using the latest version") + } + + return release.TagName, release.Assets, nil +} + +func DownloadLatestVersion(assets []Asset) error { + // Download the latest version + fmt.Println("Downloading the latest version...") + + // ok, replace this from the current version to the latest version + err := os.Rename(exe, exe+".old") + if err != nil { + return err + } + + // Download the latest version for the current OS + for _, asset := range assets { + switch runtime.GOOS { + case "windows": + if asset.Name == "katenary.exe" { + err = DownloadFile(asset.URL, exe) + } + case "linux": + switch runtime.GOARCH { + case "amd64": + if asset.Name == "katenary-linux-amd64" { + err = DownloadFile(asset.URL, exe) + } + case "arm64": + if asset.Name == "katenary-linux-arm64" { + err = DownloadFile(asset.URL, exe) + } + } + case "darwin": + if asset.Name == "katenary-darwin" { + err = DownloadFile(asset.URL, exe) + } + default: + fmt.Println("Unsupported OS") + err = errors.New("Unsupported OS") + } + } + if err == nil { + // remove the old version + os.Remove(exe + ".old") + } else { + // restore the old version + os.Rename(exe+".old", exe) + } + return err +} + +func DownloadFile(url, exe string) error { + // Download the url binary to exe path + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + fp, err := os.OpenFile(exe, os.O_WRONLY|os.O_CREATE, 0755) + if err != nil { + return err + } + defer fp.Close() + _, err = io.Copy(fp, resp.Body) + return err +} diff --git a/update/update_test.go b/update/update_test.go new file mode 100644 index 0000000..99bd9fc --- /dev/null +++ b/update/update_test.go @@ -0,0 +1,36 @@ +package update + +import ( + "fmt" + "katenary/cmd" + "os" + "testing" +) + +func TestDownloadLatestRelease(t *testing.T) { + + // Change the cmd.Version to "v0.0.0" to test the fallback to the latest release + cmd.Version = "v0.0.0" + + // change "exe" to /tmp/test-katenary + exe = "/tmp/test-katenary" + + // Now call the CheckLatestVersion function + version, assets, err := CheckLatestVersion() + + if err != nil { + t.Errorf("Error: %s", err) + } + + fmt.Println("Version found", version) + + // Touch exe binary + f, _ := os.OpenFile(exe, os.O_RDONLY|os.O_CREATE, 0755) + f.Write(nil) + f.Close() + + err = DownloadLatestVersion(assets) + if err != nil { + t.Errorf("Error: %s", err) + } +}