github.com/minio/console@v1.4.1/cmd/console/update.go (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2021 MinIO, Inc. 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Affero General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "io" 24 "net" 25 "net/http" 26 "os" 27 "runtime" 28 "strings" 29 "time" 30 31 "github.com/blang/semver/v4" 32 "github.com/cheggaaa/pb/v3" 33 "github.com/minio/cli" 34 "github.com/minio/console/pkg" 35 "github.com/minio/selfupdate" 36 ) 37 38 func getUpdateTransport(timeout time.Duration) http.RoundTripper { 39 var updateTransport http.RoundTripper = &http.Transport{ 40 Proxy: http.ProxyFromEnvironment, 41 DialContext: (&net.Dialer{ 42 Timeout: timeout, 43 KeepAlive: timeout, 44 DualStack: true, 45 }).DialContext, 46 IdleConnTimeout: timeout, 47 TLSHandshakeTimeout: timeout, 48 ExpectContinueTimeout: timeout, 49 DisableCompression: true, 50 } 51 return updateTransport 52 } 53 54 func getUpdateReaderFromURL(u string, transport http.RoundTripper) (io.ReadCloser, int64, error) { 55 clnt := &http.Client{ 56 Transport: transport, 57 } 58 req, err := http.NewRequest(http.MethodGet, u, nil) 59 if err != nil { 60 return nil, -1, err 61 } 62 63 resp, err := clnt.Do(req) 64 if err != nil { 65 return nil, -1, err 66 } 67 return resp.Body, resp.ContentLength, nil 68 } 69 70 const defaultPubKey = "RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav" 71 72 func getLatestRelease(tr http.RoundTripper) (string, error) { 73 releaseURL := "https://api.github.com/repos/minio/console/releases/latest" 74 75 body, _, err := getUpdateReaderFromURL(releaseURL, tr) 76 if err != nil { 77 return "", fmt.Errorf("unable to access github release URL %w", err) 78 } 79 defer body.Close() 80 81 lm := make(map[string]interface{}) 82 if err = json.NewDecoder(body).Decode(&lm); err != nil { 83 return "", err 84 } 85 rel, ok := lm["tag_name"].(string) 86 if !ok { 87 return "", errors.New("unable to find latest release tag") 88 } 89 return rel, nil 90 } 91 92 // update console in-place 93 var updateCmd = cli.Command{ 94 Name: "update", 95 Usage: "update console to latest release", 96 Action: updateInplace, 97 } 98 99 func updateInplace(_ *cli.Context) error { 100 transport := getUpdateTransport(30 * time.Second) 101 rel, err := getLatestRelease(transport) 102 if err != nil { 103 return err 104 } 105 106 latest, err := semver.Make(strings.TrimPrefix(rel, "v")) 107 if err != nil { 108 return err 109 } 110 111 current, err := semver.Make(pkg.Version) 112 if err != nil { 113 return err 114 } 115 116 if current.GTE(latest) { 117 fmt.Printf("You are already running the latest version v%v.\n", pkg.Version) 118 return nil 119 } 120 121 consoleBin := fmt.Sprintf("https://github.com/minio/console/releases/download/%s/console-%s-%s", rel, runtime.GOOS, runtime.GOARCH) 122 reader, length, err := getUpdateReaderFromURL(consoleBin, transport) 123 if err != nil { 124 return fmt.Errorf("unable to fetch binary from %s: %w", consoleBin, err) 125 } 126 127 minisignPubkey := os.Getenv("CONSOLE_MINISIGN_PUBKEY") 128 if minisignPubkey == "" { 129 minisignPubkey = defaultPubKey 130 } 131 132 v := selfupdate.NewVerifier() 133 if err = v.LoadFromURL(consoleBin+".minisig", minisignPubkey, transport); err != nil { 134 return fmt.Errorf("unable to fetch binary signature for %s: %w", consoleBin, err) 135 } 136 opts := selfupdate.Options{ 137 Verifier: v, 138 } 139 140 tmpl := `{{ red "Downloading:" }} {{bar . (red "[") (green "=") (red "]")}} {{speed . | rndcolor }}` 141 bar := pb.ProgressBarTemplate(tmpl).Start64(length) 142 barReader := bar.NewProxyReader(reader) 143 if err = selfupdate.Apply(barReader, opts); err != nil { 144 bar.Finish() 145 if rerr := selfupdate.RollbackError(err); rerr != nil { 146 return rerr 147 } 148 return err 149 } 150 151 bar.Finish() 152 fmt.Printf("Updated 'console' to latest release %s\n", rel) 153 return nil 154 }