commit
f490af85e8
@ -0,0 +1,86 @@ |
||||
/* |
||||
* Minio Cloud Storage, (C) 2015 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"math" |
||||
"runtime" |
||||
"strings" |
||||
|
||||
"github.com/fatih/color" |
||||
"github.com/minio/minio-xl/pkg/probe" |
||||
"github.com/olekukonko/ts" |
||||
) |
||||
|
||||
// colorizeUpdateMessage - inspired from Yeoman project npm package https://github.com/yeoman/update-notifier
|
||||
func colorizeUpdateMessage(updateString string) (string, *probe.Error) { |
||||
// initialize coloring
|
||||
cyan := color.New(color.FgCyan, color.Bold).SprintFunc() |
||||
yellow := color.New(color.FgYellow, color.Bold).SprintfFunc() |
||||
|
||||
// calculate length without color coding, due to ANSI color characters padded to actual
|
||||
// string the final length is wrong than the original string length
|
||||
line1Str := fmt.Sprintf(" Update available: ") |
||||
line2Str := fmt.Sprintf(" Run \"%s\" to update. ", updateString) |
||||
line1Length := len(line1Str) |
||||
line2Length := len(line2Str) |
||||
|
||||
// populate lines with color coding
|
||||
line1InColor := line1Str |
||||
line2InColor := fmt.Sprintf(" Run \"%s\" to update. ", cyan(updateString)) |
||||
|
||||
// calculate the rectangular box size
|
||||
maxContentWidth := int(math.Max(float64(line1Length), float64(line2Length))) |
||||
line1Rest := maxContentWidth - line1Length |
||||
line2Rest := maxContentWidth - line2Length |
||||
|
||||
terminal, err := ts.GetSize() |
||||
if err != nil { |
||||
return "", probe.NewError(err) |
||||
} |
||||
|
||||
var message string |
||||
switch { |
||||
case len(line2Str) > terminal.Col(): |
||||
message = "\n" + line1InColor + "\n" + line2InColor + "\n" |
||||
default: |
||||
// on windows terminal turn off unicode characters
|
||||
var top, bottom, sideBar string |
||||
if runtime.GOOS == "windows" { |
||||
top = yellow("*" + strings.Repeat("*", maxContentWidth) + "*") |
||||
bottom = yellow("*" + strings.Repeat("*", maxContentWidth) + "*") |
||||
sideBar = yellow("|") |
||||
} else { |
||||
// color the rectangular box, use unicode characters here
|
||||
top = yellow("┏" + strings.Repeat("━", maxContentWidth) + "┓") |
||||
bottom = yellow("┗" + strings.Repeat("━", maxContentWidth) + "┛") |
||||
sideBar = yellow("┃") |
||||
} |
||||
// fill spaces to the rest of the area
|
||||
spacePaddingLine1 := strings.Repeat(" ", line1Rest) |
||||
spacePaddingLine2 := strings.Repeat(" ", line2Rest) |
||||
|
||||
// construct the final message
|
||||
message = "\n" + top + "\n" + |
||||
sideBar + line1InColor + spacePaddingLine1 + sideBar + "\n" + |
||||
sideBar + line2InColor + spacePaddingLine2 + sideBar + "\n" + |
||||
bottom + "\n" |
||||
} |
||||
// finally print the message
|
||||
return message, nil |
||||
} |
@ -0,0 +1,203 @@ |
||||
/* |
||||
* Minio Cloud Storage, (C) 2015 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
"net/http" |
||||
"net/url" |
||||
"runtime" |
||||
"strings" |
||||
"time" |
||||
|
||||
"github.com/fatih/color" |
||||
"github.com/minio/cli" |
||||
"github.com/minio/minio-xl/pkg/probe" |
||||
) |
||||
|
||||
// Check for new software updates.
|
||||
var updateCmd = cli.Command{ |
||||
Name: "update", |
||||
Usage: "Check for new software updates.", |
||||
Action: mainUpdate, |
||||
CustomHelpTemplate: `Name: |
||||
minio {{.Name}} - {{.Usage}} |
||||
|
||||
USAGE: |
||||
minio {{.Name}} release |
||||
minio {{.Name}} experimental |
||||
|
||||
EXAMPLES: |
||||
1. Check for new official releases |
||||
$ minio {{.Name}} release |
||||
|
||||
2. Check for new experimental releases |
||||
$ minio {{.Name}} experimental |
||||
`, |
||||
} |
||||
|
||||
// updates container to hold updates json
|
||||
type updates struct { |
||||
BuildDate string |
||||
Platforms map[string]string |
||||
} |
||||
|
||||
// updateMessage container to hold update messages
|
||||
type updateMessage struct { |
||||
Update bool `json:"update"` |
||||
Download string `json:"downloadURL"` |
||||
Version string `json:"version"` |
||||
} |
||||
|
||||
// String colorized update message
|
||||
func (u updateMessage) String() string { |
||||
if u.Update { |
||||
var msg string |
||||
if runtime.GOOS == "windows" { |
||||
msg = "Download " + u.Download |
||||
} else { |
||||
msg = "Download " + u.Download |
||||
} |
||||
msg, err := colorizeUpdateMessage(msg) |
||||
fatalIf(err.Trace(msg), "Unable to colorize experimental update notification string ‘"+msg+"’.", nil) |
||||
return msg |
||||
} |
||||
updateMessage := color.New(color.FgGreen, color.Bold).SprintfFunc() |
||||
return updateMessage("You are already running the most recent version of ‘minio’.") |
||||
} |
||||
|
||||
// JSON jsonified update message
|
||||
func (u updateMessage) JSON() string { |
||||
updateMessageJSONBytes, err := json.Marshal(u) |
||||
fatalIf(probe.NewError(err), "Unable to marshal into JSON.", nil) |
||||
|
||||
return string(updateMessageJSONBytes) |
||||
} |
||||
|
||||
func getExperimentalUpdate() { |
||||
current, e := time.Parse(http.TimeFormat, minioVersion) |
||||
fatalIf(probe.NewError(e), "Unable to parse Version string as time.", nil) |
||||
|
||||
if current.IsZero() { |
||||
fatalIf(probe.NewError(errors.New("")), "Experimental updates are not supported for custom build. Version field is empty. Please download official releases from https://dl.minio.io:9000", nil) |
||||
} |
||||
|
||||
resp, err := http.Get(minioExperimentalURL) |
||||
fatalIf(probe.NewError(err), "Unable to initalize experimental URL.", nil) |
||||
|
||||
var experimentals updates |
||||
decoder := json.NewDecoder(resp.Body) |
||||
defer resp.Body.Close() |
||||
e = decoder.Decode(&experimentals) |
||||
fatalIf(probe.NewError(e), "Unable to decode experimental update notification.", nil) |
||||
|
||||
latest, e := time.Parse(http.TimeFormat, experimentals.BuildDate) |
||||
fatalIf(probe.NewError(e), "Unable to parse BuildDate.", nil) |
||||
|
||||
if latest.IsZero() { |
||||
fatalIf(probe.NewError(errors.New("")), "Unable to validate any experimental update available at this time. Please open an issue at https://github.com/minio/minio/issues", nil) |
||||
} |
||||
|
||||
minioExperimentalURLParse, err := url.Parse(minioExperimentalURL) |
||||
if err != nil { |
||||
fatalIf(probe.NewError(err), "Unable to parse URL: "+minioExperimentalURL, nil) |
||||
} |
||||
downloadURL := minioExperimentalURLParse.Scheme + "://" + minioExperimentalURLParse.Host + "/" + experimentals.Platforms[runtime.GOOS] |
||||
updateMessage := updateMessage{ |
||||
Download: downloadURL, |
||||
Version: minioVersion, |
||||
} |
||||
if latest.After(current) { |
||||
updateMessage.Update = true |
||||
} |
||||
if globalJSONFlag { |
||||
Println(updateMessage.JSON()) |
||||
} else { |
||||
Println(updateMessage) |
||||
} |
||||
} |
||||
|
||||
func getReleaseUpdate() { |
||||
current, e := time.Parse(http.TimeFormat, minioVersion) |
||||
fatalIf(probe.NewError(e), "Unable to parse Version string as time.", nil) |
||||
|
||||
if current.IsZero() { |
||||
fatalIf(probe.NewError(errors.New("")), "Updates not supported for custom build. Version field is empty. Please download official releases from https://dl.minio.io:9000", nil) |
||||
} |
||||
|
||||
resp, err := http.Get(minioUpdateURL) |
||||
fatalIf(probe.NewError(err), "Unable to initalize experimental URL.", nil) |
||||
|
||||
var releases updates |
||||
decoder := json.NewDecoder(resp.Body) |
||||
e = decoder.Decode(&releases) |
||||
fatalIf(probe.NewError(e), "Unable to decode update notification.", nil) |
||||
|
||||
latest, e := time.Parse(http.TimeFormat, releases.BuildDate) |
||||
fatalIf(probe.NewError(e), "Unable to parse BuildDate.", nil) |
||||
|
||||
if latest.IsZero() { |
||||
fatalIf(probe.NewError(errors.New("")), "Unable to validate any update available at this time. Please open an issue at https://github.com/minio/minio/issues", nil) |
||||
} |
||||
|
||||
minioUpdateURLParse, err := url.Parse(minioUpdateURL) |
||||
if err != nil { |
||||
fatalIf(probe.NewError(err), "Unable to parse URL: "+minioUpdateURL, nil) |
||||
} |
||||
downloadURL := minioUpdateURLParse.Scheme + "://" + minioUpdateURLParse.Host + "/" + releases.Platforms[runtime.GOOS] |
||||
updateMessage := updateMessage{ |
||||
Download: downloadURL, |
||||
Version: minioVersion, |
||||
} |
||||
if latest.After(current) { |
||||
updateMessage.Update = true |
||||
} |
||||
|
||||
if globalJSONFlag { |
||||
Println(updateMessage.JSON()) |
||||
} else { |
||||
Println(updateMessage) |
||||
} |
||||
} |
||||
|
||||
const ( |
||||
minioUpdateURL = "https://dl.minio.io:9000/updates/minio/updates.json" |
||||
minioExperimentalURL = "https://dl.minio.io:9000/updates/minio/experimental.json" |
||||
) |
||||
|
||||
func checkUpdateSyntax(ctx *cli.Context) { |
||||
if ctx.Args().First() == "help" || !ctx.Args().Present() { |
||||
cli.ShowCommandHelpAndExit(ctx, "update", 1) // last argument is exit code
|
||||
} |
||||
arg := strings.TrimSpace(ctx.Args().First()) |
||||
if arg != "release" && arg != "experimental" { |
||||
fatalIf(probe.NewError(errInvalidArgument), "Unrecognized argument provided.", nil) |
||||
} |
||||
} |
||||
|
||||
// mainUpdate -
|
||||
func mainUpdate(ctx *cli.Context) { |
||||
checkUpdateSyntax(ctx) |
||||
arg := strings.TrimSpace(ctx.Args().First()) |
||||
switch arg { |
||||
case "release": |
||||
getReleaseUpdate() |
||||
case "experimental": |
||||
getExperimentalUpdate() |
||||
} |
||||
} |
Loading…
Reference in new issue