mirror of https://github.com/minio/minio.git
mainUpdate: refactor to handle quiet flag properly (#3744)
This commit is contained in:
parent
c6e76160ad
commit
602dac8773
|
@ -145,14 +145,13 @@ func checkMainSyntax(c *cli.Context) {
|
|||
func checkUpdate() {
|
||||
// Do not print update messages, if quiet flag is set.
|
||||
if !globalQuiet {
|
||||
updateMsg, _, err := getReleaseUpdate(minioUpdateStableURL, 1*time.Second)
|
||||
older, downloadURL, err := getUpdateInfo(1 * time.Second)
|
||||
if err != nil {
|
||||
// Ignore any errors during getReleaseUpdate(), possibly
|
||||
// because of network errors.
|
||||
// Its OK to ignore any errors during getUpdateInfo() here.
|
||||
return
|
||||
}
|
||||
if updateMsg.Update {
|
||||
console.Println(updateMsg)
|
||||
if older > time.Duration(0) {
|
||||
console.Println(colorizeUpdateMessage(downloadURL, older))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Minio Cloud Storage, (C) 2015 Minio, Inc.
|
||||
* Minio Cloud Storage, (C) 2015, 2016, 2017 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,7 +18,7 @@ package cmd
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -36,7 +36,16 @@ var updateCmd = cli.Command{
|
|||
Name: "update",
|
||||
Usage: "Check for a new software update.",
|
||||
Action: mainUpdate,
|
||||
Flags: globalFlags,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "help, h",
|
||||
Usage: "Show this help.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "quiet",
|
||||
Usage: "Disable any update messages.",
|
||||
},
|
||||
},
|
||||
CustomHelpTemplate: `Name:
|
||||
minio {{.Name}} - {{.Usage}}
|
||||
|
||||
|
@ -46,238 +55,215 @@ USAGE:
|
|||
FLAGS:
|
||||
{{range .Flags}}{{.}}
|
||||
{{end}}
|
||||
EXAMPLES:
|
||||
1. Check for any new official release.
|
||||
$ minio {{.Name}}
|
||||
`,
|
||||
EXIT STATUS:
|
||||
0 - You are already running the most recent version.
|
||||
1 - New update is available.
|
||||
-1 - Error in getting update information.
|
||||
|
||||
VERSION:
|
||||
` + Version + `{{"\n"}}`,
|
||||
}
|
||||
|
||||
// update URL endpoints.
|
||||
const (
|
||||
minioUpdateStableURL = "https://dl.minio.io/server/minio/release"
|
||||
)
|
||||
const releaseTagTimeLayout = "2006-01-02T15-04-05Z"
|
||||
|
||||
// updateMessage container to hold update messages.
|
||||
type updateMessage struct {
|
||||
Update bool `json:"update"`
|
||||
Download string `json:"downloadURL"`
|
||||
NewerThan time.Duration `json:"newerThan"`
|
||||
}
|
||||
const minioReleaseURL = "https://dl.minio.io/server/minio/release/" + runtime.GOOS + "-" + runtime.GOARCH + "/"
|
||||
|
||||
// String colorized update message.
|
||||
func (u updateMessage) String() string {
|
||||
if !u.Update {
|
||||
updateMessage := color.New(color.FgGreen, color.Bold).SprintfFunc()
|
||||
return updateMessage("You are already running the most recent version of ‘minio’.")
|
||||
func getCurrentReleaseTime(minioVersion, minioBinaryPath string) (releaseTime time.Time, err error) {
|
||||
if releaseTime, err = time.Parse(time.RFC3339, minioVersion); err == nil {
|
||||
return releaseTime, err
|
||||
}
|
||||
msg := colorizeUpdateMessage(u.Download, u.NewerThan)
|
||||
return msg
|
||||
}
|
||||
|
||||
func parseReleaseData(data string) (time.Time, error) {
|
||||
releaseStr := strings.Fields(data)
|
||||
if len(releaseStr) < 2 {
|
||||
return time.Time{}, errors.New("Update data malformed")
|
||||
}
|
||||
releaseDate := releaseStr[1]
|
||||
releaseDateSplits := strings.SplitN(releaseDate, ".", 3)
|
||||
if len(releaseDateSplits) < 3 {
|
||||
return time.Time{}, (errors.New("Update data malformed"))
|
||||
}
|
||||
if releaseDateSplits[0] != globalMinioDefaultOwnerID {
|
||||
return time.Time{}, (errors.New("Update data malformed, missing minio tag"))
|
||||
}
|
||||
// "OFFICIAL" tag is still kept for backward compatibility.
|
||||
// We should remove this for the next release.
|
||||
if releaseDateSplits[1] != "RELEASE" && releaseDateSplits[1] != "OFFICIAL" {
|
||||
return time.Time{}, (errors.New("Update data malformed, missing RELEASE tag"))
|
||||
}
|
||||
dateSplits := strings.SplitN(releaseDateSplits[2], "T", 2)
|
||||
if len(dateSplits) < 2 {
|
||||
return time.Time{}, (errors.New("Update data malformed, not in modified RFC3359 form"))
|
||||
}
|
||||
dateSplits[1] = strings.Replace(dateSplits[1], "-", ":", -1)
|
||||
date := strings.Join(dateSplits, "T")
|
||||
|
||||
parsedDate, err := time.Parse(time.RFC3339, date)
|
||||
// Looks like version is minio non-standard, we use minio binary's ModTime as release time.
|
||||
fi, err := os.Stat(minioBinaryPath)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
return parsedDate, nil
|
||||
}
|
||||
|
||||
// User Agent should always following the below style.
|
||||
// Please open an issue to discuss any new changes here.
|
||||
//
|
||||
// Minio (OS; ARCH) APP/VER APP/VER
|
||||
var (
|
||||
userAgentSuffix = "Minio/" + Version + " " + "Minio/" + ReleaseTag + " " + "Minio/" + CommitID
|
||||
)
|
||||
|
||||
// Check if the operating system is a docker container.
|
||||
func isDocker() bool {
|
||||
cgroup, err := ioutil.ReadFile("/proc/self/cgroup")
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
errorIf(err, "Unable to read `cgroup` file.")
|
||||
}
|
||||
|
||||
return bytes.Contains(cgroup, []byte("docker"))
|
||||
}
|
||||
|
||||
// Check if the minio server binary was built with source.
|
||||
func isSourceBuild() bool {
|
||||
return Version == goGetTag
|
||||
}
|
||||
|
||||
// Fetch the current version of the Minio server binary.
|
||||
func getCurrentMinioVersion() (current time.Time, err error) {
|
||||
// For development builds we check for binary modTime
|
||||
// to validate against latest minio server release.
|
||||
if Version != goGetTag {
|
||||
// Parse current minio version into RFC3339.
|
||||
current, err = time.Parse(time.RFC3339, Version)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
return current, nil
|
||||
} // else {
|
||||
// For all development builds through `go get`.
|
||||
// fall back to looking for version of the build
|
||||
// date of the binary itself.
|
||||
var fi os.FileInfo
|
||||
fi, err = os.Stat(os.Args[0])
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
return fi.ModTime(), nil
|
||||
}
|
||||
|
||||
// verify updates for releases.
|
||||
func getReleaseUpdate(updateURL string, duration time.Duration) (updateMsg updateMessage, errMsg string, err error) {
|
||||
// Construct a new update url.
|
||||
newUpdateURLPrefix := updateURL + "/" + runtime.GOOS + "-" + runtime.GOARCH
|
||||
newUpdateURL := newUpdateURLPrefix + "/minio.shasum"
|
||||
|
||||
// Get the downloadURL.
|
||||
var downloadURL string
|
||||
if isDocker() {
|
||||
downloadURL = "docker pull minio/minio"
|
||||
err = fmt.Errorf("Unable to get ModTime of %s. %s", minioBinaryPath, err)
|
||||
} else {
|
||||
switch runtime.GOOS {
|
||||
case globalWindowsOSName:
|
||||
// For windows.
|
||||
downloadURL = newUpdateURLPrefix + "/minio.exe"
|
||||
default:
|
||||
// For all other operating systems.
|
||||
downloadURL = newUpdateURLPrefix + "/minio"
|
||||
}
|
||||
releaseTime = fi.ModTime().UTC()
|
||||
}
|
||||
|
||||
// Initialize update message.
|
||||
updateMsg = updateMessage{
|
||||
Download: downloadURL,
|
||||
return releaseTime, err
|
||||
}
|
||||
|
||||
// GetCurrentReleaseTime - returns this process's release time. If it is official minio version,
|
||||
// parsed version is returned else minio binary's mod time is returned.
|
||||
func GetCurrentReleaseTime() (releaseTime time.Time, err error) {
|
||||
return getCurrentReleaseTime(Version, os.Args[0])
|
||||
}
|
||||
|
||||
func isDocker(cgroupFile string) (bool, error) {
|
||||
cgroup, err := ioutil.ReadFile(cgroupFile)
|
||||
if os.IsNotExist(err) {
|
||||
err = nil
|
||||
}
|
||||
|
||||
// Instantiate a new client with 3 sec timeout.
|
||||
return bytes.Contains(cgroup, []byte("docker")), err
|
||||
}
|
||||
|
||||
// IsDocker - returns if the environment is docker or not.
|
||||
func IsDocker() bool {
|
||||
found, err := isDocker("/proc/self/cgroup")
|
||||
if err != nil {
|
||||
console.Fatalf("Error in docker check: %s", err)
|
||||
}
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
func isSourceBuild(minioVersion string) bool {
|
||||
_, err := time.Parse(time.RFC3339, minioVersion)
|
||||
return err != nil
|
||||
}
|
||||
|
||||
// IsSourceBuild - returns if this binary is made from source or not.
|
||||
func IsSourceBuild() bool {
|
||||
return isSourceBuild(Version)
|
||||
}
|
||||
|
||||
// DO NOT CHANGE USER AGENT STYLE.
|
||||
// The style should be
|
||||
// Minio (<OS>; <ARCH>[; docker][; source]) Minio/<VERSION> Minio/<RELEASE-TAG> Minio/<COMMIT-ID>
|
||||
//
|
||||
// For any change here should be discussed by openning an issue at https://github.com/minio/minio/issues.
|
||||
func getUserAgent() string {
|
||||
userAgent := "Minio (" + runtime.GOOS + "; " + runtime.GOARCH
|
||||
if IsDocker() {
|
||||
userAgent += "; docker"
|
||||
}
|
||||
if IsSourceBuild() {
|
||||
userAgent += "; source"
|
||||
}
|
||||
userAgent += ") " + " Minio/" + Version + " Minio/" + ReleaseTag + " Minio/" + CommitID
|
||||
|
||||
return userAgent
|
||||
}
|
||||
|
||||
func downloadReleaseData(releaseChecksumURL string, timeout time.Duration) (data string, err error) {
|
||||
req, err := http.NewRequest("GET", releaseChecksumURL, nil)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
req.Header.Set("User-Agent", getUserAgent())
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: duration,
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
current, err := getCurrentMinioVersion()
|
||||
if err != nil {
|
||||
errMsg = "Unable to fetch the current version of Minio server."
|
||||
return
|
||||
}
|
||||
|
||||
// Initialize new request.
|
||||
req, err := http.NewRequest("GET", newUpdateURL, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
userAgentPrefix := func() string {
|
||||
prefix := "Minio (" + runtime.GOOS + "; " + runtime.GOARCH
|
||||
// if its a source build.
|
||||
if isSourceBuild() {
|
||||
if isDocker() {
|
||||
prefix = prefix + "; " + "docker; source) "
|
||||
} else {
|
||||
prefix = prefix + "; " + "source) "
|
||||
}
|
||||
return prefix
|
||||
} // else { not source.
|
||||
if isDocker() {
|
||||
prefix = prefix + "; " + "docker) "
|
||||
} else {
|
||||
prefix = prefix + ") "
|
||||
}
|
||||
return prefix
|
||||
}()
|
||||
|
||||
// Set user agent.
|
||||
req.Header.Set("User-Agent", userAgentPrefix+" "+userAgentSuffix)
|
||||
|
||||
// Fetch new update.
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
return data, err
|
||||
}
|
||||
if resp == nil {
|
||||
return data, fmt.Errorf("No response from server to download URL %s", releaseChecksumURL)
|
||||
}
|
||||
|
||||
// Verify if we have a valid http response i.e http.StatusOK.
|
||||
if resp != nil {
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
errMsg = "Failed to retrieve update notice."
|
||||
err = errors.New("http status : " + resp.Status)
|
||||
return
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return data, fmt.Errorf("Error downloading URL %s. Response: %v", releaseChecksumURL, resp.Status)
|
||||
}
|
||||
|
||||
dataBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return data, fmt.Errorf("Error reading response. %s", err)
|
||||
}
|
||||
|
||||
data = string(dataBytes)
|
||||
return data, err
|
||||
}
|
||||
|
||||
// DownloadReleaseData - downloads release data from minio official server.
|
||||
func DownloadReleaseData(timeout time.Duration) (data string, err error) {
|
||||
return downloadReleaseData(minioReleaseURL+"minio.shasum", timeout)
|
||||
}
|
||||
|
||||
func parseReleaseData(data string) (releaseTime time.Time, err error) {
|
||||
fields := strings.Fields(data)
|
||||
if len(fields) != 2 {
|
||||
err = fmt.Errorf("Unknown release data `%s`", data)
|
||||
return releaseTime, err
|
||||
}
|
||||
|
||||
releaseInfo := fields[1]
|
||||
if fields = strings.Split(releaseInfo, "."); len(fields) != 3 {
|
||||
err = fmt.Errorf("Unknown release information `%s`", releaseInfo)
|
||||
return releaseTime, err
|
||||
}
|
||||
|
||||
if !(fields[0] == "minio" && fields[1] == "RELEASE") {
|
||||
err = fmt.Errorf("Unknown release '%s'", releaseInfo)
|
||||
return releaseTime, err
|
||||
}
|
||||
|
||||
releaseTime, err = time.Parse(releaseTagTimeLayout, fields[2])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Unknown release time format. %s", err)
|
||||
}
|
||||
|
||||
return releaseTime, err
|
||||
}
|
||||
|
||||
func getLatestReleaseTime(timeout time.Duration) (releaseTime time.Time, err error) {
|
||||
data, err := DownloadReleaseData(timeout)
|
||||
if err != nil {
|
||||
return releaseTime, err
|
||||
}
|
||||
|
||||
return parseReleaseData(data)
|
||||
}
|
||||
|
||||
func getDownloadURL() (downloadURL string) {
|
||||
if IsDocker() {
|
||||
return "docker pull minio/minio"
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
return minioReleaseURL + "minio.exe"
|
||||
}
|
||||
|
||||
return minioReleaseURL + "minio"
|
||||
}
|
||||
|
||||
func getUpdateInfo(timeout time.Duration) (older time.Duration, downloadURL string, err error) {
|
||||
currentReleaseTime, err := GetCurrentReleaseTime()
|
||||
if err != nil {
|
||||
return older, downloadURL, err
|
||||
}
|
||||
|
||||
latestReleaseTime, err := getLatestReleaseTime(timeout)
|
||||
if err != nil {
|
||||
return older, downloadURL, err
|
||||
}
|
||||
|
||||
if latestReleaseTime.After(currentReleaseTime) {
|
||||
older = latestReleaseTime.Sub(currentReleaseTime)
|
||||
downloadURL = getDownloadURL()
|
||||
}
|
||||
|
||||
return older, downloadURL, nil
|
||||
}
|
||||
|
||||
func mainUpdate(ctx *cli.Context) {
|
||||
if len(ctx.Args()) != 0 {
|
||||
cli.ShowCommandHelpAndExit(ctx, "update", -1)
|
||||
}
|
||||
|
||||
quiet := ctx.Bool("quiet") || ctx.GlobalBool("quiet")
|
||||
quietPrintln := func(args ...interface{}) {
|
||||
if !quiet {
|
||||
console.Println(args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Read the response body.
|
||||
updateBody, err := ioutil.ReadAll(resp.Body)
|
||||
older, downloadURL, err := getUpdateInfo(10 * time.Second)
|
||||
if err != nil {
|
||||
errMsg = "Failed to retrieve update notice. Please try again later."
|
||||
return
|
||||
quietPrintln(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
errMsg = "Failed to retrieve update notice. Please try again later. Please report this issue at https://github.com/minio/minio/issues"
|
||||
|
||||
// Parse the date if its valid.
|
||||
latest, err := parseReleaseData(string(updateBody))
|
||||
if err != nil {
|
||||
return
|
||||
if older != time.Duration(0) {
|
||||
quietPrintln(colorizeUpdateMessage(downloadURL, older))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Verify if the date is not zero.
|
||||
if latest.IsZero() {
|
||||
err = errors.New("Release date cannot be zero. Please report this issue at https://github.com/minio/minio/issues")
|
||||
return
|
||||
}
|
||||
|
||||
// Is the update latest?.
|
||||
if latest.After(current) {
|
||||
updateMsg.Update = true
|
||||
updateMsg.NewerThan = latest.Sub(current)
|
||||
}
|
||||
|
||||
// Return update message.
|
||||
return updateMsg, "", nil
|
||||
}
|
||||
|
||||
// main entry point for update command.
|
||||
func mainUpdate(ctx *cli.Context) {
|
||||
// Initialization routine, such as config loading, enable logging, ..
|
||||
minioInit(ctx)
|
||||
|
||||
if globalQuiet {
|
||||
return
|
||||
}
|
||||
|
||||
// Check for update.
|
||||
var updateMsg updateMessage
|
||||
var errMsg string
|
||||
var err error
|
||||
var secs = time.Second * 3
|
||||
updateMsg, errMsg, err = getReleaseUpdate(minioUpdateStableURL, secs)
|
||||
fatalIf(err, errMsg)
|
||||
console.Println(updateMsg)
|
||||
colorSprintf := color.New(color.FgGreen, color.Bold).SprintfFunc()
|
||||
quietPrintln(colorSprintf("You are already running the most recent version of ‘minio’."))
|
||||
os.Exit(0)
|
||||
}
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
// +build linux darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2016 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 cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Validate when release versions are properly set.
|
||||
func TestReleaseUpdateVersion(t *testing.T) {
|
||||
Version = "2016-10-06T00:08:32Z"
|
||||
ReleaseTag = "RELEASE.2016-10-06T00-08-32Z"
|
||||
CommitID = "d1c38ba8f0b3aecdf9b932c087dd65c21eebac33"
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "fbe246edbd382902db9a4035df7dce8cb441357d minio.RELEASE.2016-10-07T01-16-39Z")
|
||||
}))
|
||||
userAgentSuffix = "Minio/" + Version + " " + "Minio/" + ReleaseTag + " " + "Minio/" + CommitID
|
||||
defer ts.Close()
|
||||
testCases := []struct {
|
||||
updateURL string
|
||||
updateMsg updateMessage
|
||||
errMsg string
|
||||
shouldPass bool
|
||||
}{
|
||||
{
|
||||
updateURL: ts.URL,
|
||||
updateMsg: updateMessage{
|
||||
Download: ts.URL + "/" + runtime.GOOS + "-" + runtime.GOARCH + "/minio",
|
||||
Update: true,
|
||||
NewerThan: 90487000000000,
|
||||
},
|
||||
errMsg: "",
|
||||
shouldPass: true,
|
||||
},
|
||||
}
|
||||
|
||||
// Validates all the errors reported.
|
||||
for i, testCase := range testCases {
|
||||
updateMsg, errMsg, err := getReleaseUpdate(testCase.updateURL, time.Second*1)
|
||||
if testCase.shouldPass && err != nil {
|
||||
t.Errorf("Test %d: Unable to fetch release update %s", i+1, err)
|
||||
}
|
||||
if errMsg != testCase.errMsg {
|
||||
t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.errMsg, errMsg)
|
||||
}
|
||||
if !reflect.DeepEqual(updateMsg, testCase.updateMsg) {
|
||||
t.Errorf("Test %d: Expected %#v, got %#v", i+1, testCase.updateMsg, updateMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseUpdate(t *testing.T) {
|
||||
Version = "DEVELOPMENT.GOGET"
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "Hello, client")
|
||||
}))
|
||||
defer ts.Close()
|
||||
testCases := []struct {
|
||||
updateURL string
|
||||
updateMsg updateMessage
|
||||
errMsg string
|
||||
shouldPass bool
|
||||
}{
|
||||
{
|
||||
updateURL: ts.URL,
|
||||
updateMsg: updateMessage{
|
||||
Download: ts.URL + "/" + runtime.GOOS + "-" + runtime.GOARCH + "/minio",
|
||||
},
|
||||
errMsg: "Failed to retrieve update notice. Please try again later. Please report this issue at https://github.com/minio/minio/issues",
|
||||
shouldPass: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Validates all the errors reported.
|
||||
for i, testCase := range testCases {
|
||||
updateMsg, errMsg, err := getReleaseUpdate(testCase.updateURL, time.Second*1)
|
||||
if testCase.shouldPass && err != nil {
|
||||
t.Errorf("Test %d: Unable to fetch release update %s", i+1, err)
|
||||
}
|
||||
if errMsg != testCase.errMsg {
|
||||
t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.errMsg, errMsg)
|
||||
}
|
||||
if !reflect.DeepEqual(updateMsg, testCase.updateMsg) {
|
||||
t.Errorf("Test %d: Expected %#v, got %#v", i+1, testCase.updateMsg, updateMsg)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
* Minio Cloud Storage, (C) 2017 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 cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGetCurrentReleaseTime(t *testing.T) {
|
||||
minioVersion1 := time.Now().UTC().Format(time.RFC3339)
|
||||
releaseTime1, _ := time.Parse(time.RFC3339, minioVersion1)
|
||||
|
||||
minioVersion2 := "DEVELOPMENT.GOGET"
|
||||
tmpfile, err := ioutil.TempFile("", "get-current-release-time-testcase")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create temporary file. %s", err)
|
||||
}
|
||||
defer os.Remove(tmpfile.Name())
|
||||
minioBinaryPath2 := tmpfile.Name()
|
||||
fi, err := tmpfile.Stat()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to get temporary file info. %s", err)
|
||||
}
|
||||
if err = tmpfile.Close(); err != nil {
|
||||
t.Fatalf("Unable to create temporary file. %s", err)
|
||||
}
|
||||
releaseTime2 := fi.ModTime().UTC()
|
||||
|
||||
errorMessage1 := "Unable to get ModTime of . stat : no such file or directory"
|
||||
if runtime.GOOS == "windows" {
|
||||
errorMessage1 = "Unable to get ModTime of . Lstat : The system cannot find the path specified."
|
||||
}
|
||||
|
||||
errorMessage2 := "Unable to get ModTime of non-existing-file. stat non-existing-file: no such file or directory"
|
||||
if runtime.GOOS == "windows" {
|
||||
errorMessage2 = "Unable to get ModTime of non-existing-file. GetFileAttributesEx non-existing-file: The system cannot find the file specified."
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
minioVersion string
|
||||
minioBinaryPath string
|
||||
expectedResult time.Time
|
||||
expectedErr error
|
||||
}{
|
||||
{minioVersion1, "", releaseTime1, nil},
|
||||
{minioVersion1, minioBinaryPath2, releaseTime1, nil},
|
||||
{minioVersion2, minioBinaryPath2, releaseTime2, nil},
|
||||
{"junk", minioBinaryPath2, releaseTime2, nil},
|
||||
{"3.2.0", minioBinaryPath2, releaseTime2, nil},
|
||||
{minioVersion2, "", time.Time{}, fmt.Errorf(errorMessage1)},
|
||||
{"junk", "non-existing-file", time.Time{}, fmt.Errorf(errorMessage2)},
|
||||
{"3.2.0", "non-existing-file", time.Time{}, fmt.Errorf(errorMessage2)},
|
||||
}
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
testCases = append(testCases, struct {
|
||||
minioVersion string
|
||||
minioBinaryPath string
|
||||
expectedResult time.Time
|
||||
expectedErr error
|
||||
}{"3.2a", "/proc/1/cwd", time.Time{}, fmt.Errorf("Unable to get ModTime of /proc/1/cwd. stat /proc/1/cwd: permission denied")})
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
result, err := getCurrentReleaseTime(testCase.minioVersion, testCase.minioBinaryPath)
|
||||
if testCase.expectedErr == nil {
|
||||
if err != nil {
|
||||
t.Fatalf("error: expected: %v, got: %v", testCase.expectedErr, err)
|
||||
}
|
||||
} else if err == nil {
|
||||
t.Fatalf("error: expected: %v, got: %v", testCase.expectedErr, err)
|
||||
} else if testCase.expectedErr.Error() != err.Error() {
|
||||
t.Fatalf("error: expected: %v, got: %v", testCase.expectedErr, err)
|
||||
}
|
||||
|
||||
if !testCase.expectedResult.Equal(result) {
|
||||
t.Fatalf("result: expected: %v, got: %v", testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsDocker(t *testing.T) {
|
||||
createTempFile := func(content string) string {
|
||||
tmpfile, err := ioutil.TempFile("", "isdocker-testcase")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create temporary file. %s", err)
|
||||
}
|
||||
if _, err = tmpfile.Write([]byte(content)); err != nil {
|
||||
t.Fatalf("Unable to create temporary file. %s", err)
|
||||
}
|
||||
if err = tmpfile.Close(); err != nil {
|
||||
t.Fatalf("Unable to create temporary file. %s", err)
|
||||
}
|
||||
return tmpfile.Name()
|
||||
}
|
||||
|
||||
filename1 := createTempFile(`11:pids:/user.slice/user-1000.slice/user@1000.service
|
||||
10:blkio:/
|
||||
9:hugetlb:/
|
||||
8:perf_event:/
|
||||
7:cpuset:/
|
||||
6:devices:/user.slice
|
||||
5:net_cls,net_prio:/
|
||||
4:cpu,cpuacct:/
|
||||
3:memory:/user/bala/0
|
||||
2:freezer:/user/bala/0
|
||||
1:name=systemd:/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service
|
||||
`)
|
||||
defer os.Remove(filename1)
|
||||
filename2 := createTempFile(`14:name=systemd:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
|
||||
13:pids:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
|
||||
12:hugetlb:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
|
||||
11:net_prio:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
|
||||
10:perf_event:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
|
||||
9:net_cls:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
|
||||
8:freezer:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
|
||||
7:devices:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
|
||||
6:memory:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
|
||||
5:blkio:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
|
||||
4:cpuacct:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
|
||||
3:cpu:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
|
||||
2:cpuset:/docker/d5eb950884d828237f60f624ff575a1a7a4daa28a8d4d750040527ed9545e727
|
||||
1:name=openrc:/docker
|
||||
`)
|
||||
defer os.Remove(filename2)
|
||||
|
||||
testCases := []struct {
|
||||
filename string
|
||||
expectedResult bool
|
||||
expectedErr error
|
||||
}{
|
||||
{"", false, nil},
|
||||
{"/tmp/non-existing-file", false, nil},
|
||||
{filename1, false, nil},
|
||||
{filename2, true, nil},
|
||||
}
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
testCases = append(testCases, struct {
|
||||
filename string
|
||||
expectedResult bool
|
||||
expectedErr error
|
||||
}{"/proc/1/cwd", false, fmt.Errorf("open /proc/1/cwd: permission denied")})
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
result, err := isDocker(testCase.filename)
|
||||
if testCase.expectedErr == nil {
|
||||
if err != nil {
|
||||
t.Fatalf("error: expected: %v, got: %v", testCase.expectedErr, err)
|
||||
}
|
||||
} else if err == nil {
|
||||
t.Fatalf("error: expected: %v, got: %v", testCase.expectedErr, err)
|
||||
} else if testCase.expectedErr.Error() != err.Error() {
|
||||
t.Fatalf("error: expected: %v, got: %v", testCase.expectedErr, err)
|
||||
}
|
||||
|
||||
if testCase.expectedResult != result {
|
||||
t.Fatalf("result: expected: %v, got: %v", testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSourceBuild(t *testing.T) {
|
||||
testCases := []struct {
|
||||
minioVersion string
|
||||
expectedResult bool
|
||||
}{
|
||||
{time.Now().UTC().Format(time.RFC3339), false},
|
||||
{"DEVELOPMENT.GOGET", true},
|
||||
{"junk", true},
|
||||
{"3.2.4", true},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
result := isSourceBuild(testCase.minioVersion)
|
||||
if testCase.expectedResult != result {
|
||||
t.Fatalf("expected: %v, got: %v", testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadReleaseData(t *testing.T) {
|
||||
httpServer1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
defer httpServer1.Close()
|
||||
httpServer2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "fbe246edbd382902db9a4035df7dce8cb441357d minio.RELEASE.2016-10-07T01-16-39Z")
|
||||
}))
|
||||
defer httpServer2.Close()
|
||||
httpServer3 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "", http.StatusNotFound)
|
||||
}))
|
||||
defer httpServer3.Close()
|
||||
|
||||
testCases := []struct {
|
||||
releaseChecksumURL string
|
||||
expectedResult string
|
||||
expectedErr error
|
||||
}{
|
||||
{httpServer1.URL, "", nil},
|
||||
{httpServer2.URL, "fbe246edbd382902db9a4035df7dce8cb441357d minio.RELEASE.2016-10-07T01-16-39Z\n", nil},
|
||||
{httpServer3.URL, "", fmt.Errorf("Error downloading URL " + httpServer3.URL + ". Response: 404 Not Found")},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
result, err := downloadReleaseData(testCase.releaseChecksumURL, 1*time.Second)
|
||||
if testCase.expectedErr == nil {
|
||||
if err != nil {
|
||||
t.Fatalf("error: expected: %v, got: %v", testCase.expectedErr, err)
|
||||
}
|
||||
} else if err == nil {
|
||||
t.Fatalf("error: expected: %v, got: %v", testCase.expectedErr, err)
|
||||
} else if testCase.expectedErr.Error() != err.Error() {
|
||||
t.Fatalf("error: expected: %v, got: %v", testCase.expectedErr, err)
|
||||
}
|
||||
|
||||
if testCase.expectedResult != result {
|
||||
t.Fatalf("result: expected: %v, got: %v", testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseReleaseData(t *testing.T) {
|
||||
releaseTime, _ := time.Parse(releaseTagTimeLayout, "2016-10-07T01-16-39Z")
|
||||
testCases := []struct {
|
||||
data string
|
||||
expectedResult time.Time
|
||||
expectedErr error
|
||||
}{
|
||||
{"more than two fields", time.Time{}, fmt.Errorf("Unknown release data `more than two fields`")},
|
||||
{"more than", time.Time{}, fmt.Errorf("Unknown release information `than`")},
|
||||
{"more than.two.fields", time.Time{}, fmt.Errorf("Unknown release 'than.two.fields'")},
|
||||
{"more minio.RELEASE.fields", time.Time{}, fmt.Errorf(`Unknown release time format. parsing time "fields" as "2006-01-02T15-04-05Z": cannot parse "fields" as "2006"`)},
|
||||
{"more minio.RELEASE.2016-10-07T01-16-39Z", releaseTime, nil},
|
||||
{"fbe246edbd382902db9a4035df7dce8cb441357d minio.RELEASE.2016-10-07T01-16-39Z\n", releaseTime, nil},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
result, err := parseReleaseData(testCase.data)
|
||||
if testCase.expectedErr == nil {
|
||||
if err != nil {
|
||||
t.Fatalf("error: expected: %v, got: %v", testCase.expectedErr, err)
|
||||
}
|
||||
} else if err == nil {
|
||||
t.Fatalf("error: expected: %v, got: %v", testCase.expectedErr, err)
|
||||
} else if testCase.expectedErr.Error() != err.Error() {
|
||||
t.Fatalf("error: expected: %v, got: %v", testCase.expectedErr, err)
|
||||
}
|
||||
|
||||
if !testCase.expectedResult.Equal(result) {
|
||||
t.Fatalf("result: expected: %v, got: %v", testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2016 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 cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Validate when release versions are properly set.
|
||||
func TestReleaseUpdateVersion(t *testing.T) {
|
||||
Version = "2016-10-06T00:08:32Z"
|
||||
ReleaseTag = "RELEASE.2016-10-06T00-08-32Z"
|
||||
CommitID = "d1c38ba8f0b3aecdf9b932c087dd65c21eebac33"
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "fbe246edbd382902db9a4035df7dce8cb441357d minio.RELEASE.2016-10-07T01-16-39Z")
|
||||
}))
|
||||
userAgentSuffix = "Minio/" + Version + " " + "Minio/" + ReleaseTag + " " + "Minio/" + CommitID
|
||||
defer ts.Close()
|
||||
testCases := []struct {
|
||||
updateURL string
|
||||
updateMsg updateMessage
|
||||
errMsg string
|
||||
shouldPass bool
|
||||
}{
|
||||
{
|
||||
updateURL: ts.URL,
|
||||
updateMsg: updateMessage{
|
||||
Download: ts.URL + "/" + runtime.GOOS + "-" + runtime.GOARCH + "/minio.exe",
|
||||
Update: true,
|
||||
NewerThan: 90487000000000,
|
||||
},
|
||||
errMsg: "",
|
||||
shouldPass: true,
|
||||
},
|
||||
}
|
||||
|
||||
// Validates all the errors reported.
|
||||
for i, testCase := range testCases {
|
||||
updateMsg, errMsg, err := getReleaseUpdate(testCase.updateURL, time.Second*1)
|
||||
if testCase.shouldPass && err != nil {
|
||||
t.Errorf("Test %d: Unable to fetch release update %s", i+1, err)
|
||||
}
|
||||
if errMsg != testCase.errMsg {
|
||||
t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.errMsg, errMsg)
|
||||
}
|
||||
if !reflect.DeepEqual(updateMsg, testCase.updateMsg) {
|
||||
t.Errorf("Test %d: Expected %#v, got %#v", i+1, testCase.updateMsg, updateMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseUpdate(t *testing.T) {
|
||||
Version = "DEVELOPMENT.GOGET"
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "Hello, client")
|
||||
}))
|
||||
defer ts.Close()
|
||||
testCases := []struct {
|
||||
updateURL string
|
||||
updateMsg updateMessage
|
||||
errMsg string
|
||||
shouldPass bool
|
||||
}{
|
||||
{
|
||||
updateURL: ts.URL,
|
||||
updateMsg: updateMessage{
|
||||
Download: ts.URL + "/" + runtime.GOOS + "-" + runtime.GOARCH + "/minio.exe",
|
||||
},
|
||||
errMsg: "Failed to retrieve update notice. Please try again later. Please report this issue at https://github.com/minio/minio/issues",
|
||||
shouldPass: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Validates all the errors reported.
|
||||
for i, testCase := range testCases {
|
||||
updateMsg, errMsg, err := getReleaseUpdate(testCase.updateURL, time.Second*1)
|
||||
if testCase.shouldPass && err != nil {
|
||||
t.Errorf("Test %d: Unable to fetch release update %s", i+1, err)
|
||||
}
|
||||
if errMsg != testCase.errMsg {
|
||||
t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.errMsg, errMsg)
|
||||
}
|
||||
if !reflect.DeepEqual(updateMsg, testCase.updateMsg) {
|
||||
t.Errorf("Test %d: Expected %#v, got %#v", i+1, testCase.updateMsg, updateMsg)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,12 +24,11 @@ import (
|
|||
|
||||
// Tests update notifier string builder.
|
||||
func TestUpdateNotifier(t *testing.T) {
|
||||
updateMsg := minioUpdateStableURL
|
||||
colorUpdateMsg := colorizeUpdateMessage(updateMsg, time.Duration(72*time.Hour))
|
||||
if strings.Index(colorUpdateMsg, "minutes") == -1 {
|
||||
colorUpdateMsg := colorizeUpdateMessage(minioReleaseURL, time.Duration(72*time.Hour))
|
||||
if !strings.Contains(colorUpdateMsg, "minutes") {
|
||||
t.Fatal("Duration string not found in colorized update message", colorUpdateMsg)
|
||||
}
|
||||
if strings.Index(colorUpdateMsg, updateMsg) == -1 {
|
||||
t.Fatal("Update message not found in colorized update message", updateMsg)
|
||||
if !strings.Contains(colorUpdateMsg, minioReleaseURL) {
|
||||
t.Fatal("Update message not found in colorized update message", minioReleaseURL)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue