mainUpdate: refactor to handle quiet flag properly (#3744)

master
Bala FA 8 years ago committed by Harshavardhana
parent c6e76160ad
commit 602dac8773
  1. 9
      cmd/main.go
  2. 328
      cmd/update-main.go
  3. 109
      cmd/update-main_nix_test.go
  4. 275
      cmd/update-main_test.go
  5. 109
      cmd/update-main_windows_test.go
  6. 9
      cmd/update-notifier_test.go

@ -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"
const minioReleaseURL = "https://dl.minio.io/server/minio/release/" + runtime.GOOS + "-" + runtime.GOARCH + "/"
// updateMessage container to hold update messages.
type updateMessage struct {
Update bool `json:"update"`
Download string `json:"downloadURL"`
NewerThan time.Duration `json:"newerThan"`
func getCurrentReleaseTime(minioVersion, minioBinaryPath string) (releaseTime time.Time, err error) {
if releaseTime, err = time.Parse(time.RFC3339, minioVersion); err == nil {
return releaseTime, err
}
// 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’.")
// Looks like version is minio non-standard, we use minio binary's ModTime as release time.
fi, err := os.Stat(minioBinaryPath)
if err != nil {
err = fmt.Errorf("Unable to get ModTime of %s. %s", minioBinaryPath, err)
} else {
releaseTime = fi.ModTime().UTC()
}
msg := colorizeUpdateMessage(u.Download, u.NewerThan)
return msg
return releaseTime, err
}
func parseReleaseData(data string) (time.Time, error) {
releaseStr := strings.Fields(data)
if len(releaseStr) < 2 {
return time.Time{}, errors.New("Update data malformed")
// 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])
}
releaseDate := releaseStr[1]
releaseDateSplits := strings.SplitN(releaseDate, ".", 3)
if len(releaseDateSplits) < 3 {
return time.Time{}, (errors.New("Update data malformed"))
func isDocker(cgroupFile string) (bool, error) {
cgroup, err := ioutil.ReadFile(cgroupFile)
if os.IsNotExist(err) {
err = nil
}
if releaseDateSplits[0] != globalMinioDefaultOwnerID {
return time.Time{}, (errors.New("Update data malformed, missing minio tag"))
return bytes.Contains(cgroup, []byte("docker")), err
}
// "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"))
// 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)
}
dateSplits := strings.SplitN(releaseDateSplits[2], "T", 2)
if len(dateSplits) < 2 {
return time.Time{}, (errors.New("Update data malformed, not in modified RFC3359 form"))
return found
}
dateSplits[1] = strings.Replace(dateSplits[1], "-", ":", -1)
date := strings.Join(dateSplits, "T")
parsedDate, err := time.Parse(time.RFC3339, date)
if err != nil {
return time.Time{}, err
func isSourceBuild(minioVersion string) bool {
_, err := time.Parse(time.RFC3339, minioVersion)
return err != nil
}
return parsedDate, nil
// IsSourceBuild - returns if this binary is made from source or not.
func IsSourceBuild() bool {
return isSourceBuild(Version)
}
// User Agent should always following the below style.
// Please open an issue to discuss any new changes here.
// DO NOT CHANGE USER AGENT STYLE.
// The style should be
// Minio (<OS>; <ARCH>[; docker][; source]) Minio/<VERSION> Minio/<RELEASE-TAG> Minio/<COMMIT-ID>
//
// Minio (OS; ARCH) APP/VER APP/VER
var (
userAgentSuffix = "Minio/" + Version + " " + "Minio/" + ReleaseTag + " " + "Minio/" + CommitID
)
// 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
// 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 userAgent
}
return bytes.Contains(cgroup, []byte("docker"))
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())
// Check if the minio server binary was built with source.
func isSourceBuild() bool {
return Version == goGetTag
client := &http.Client{
Timeout: timeout,
}
// 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])
resp, err := client.Do(req)
if err != nil {
return time.Time{}, err
return data, err
}
return fi.ModTime(), nil
if resp == nil {
return data, fmt.Errorf("No response from server to download URL %s", releaseChecksumURL)
}
// 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"
} else {
switch runtime.GOOS {
case globalWindowsOSName:
// For windows.
downloadURL = newUpdateURLPrefix + "/minio.exe"
default:
// For all other operating systems.
downloadURL = newUpdateURLPrefix + "/minio"
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)
}
// Initialize update message.
updateMsg = updateMessage{
Download: downloadURL,
data = string(dataBytes)
return data, err
}
// Instantiate a new client with 3 sec timeout.
client := &http.Client{
Timeout: duration,
// DownloadReleaseData - downloads release data from minio official server.
func DownloadReleaseData(timeout time.Duration) (data string, err error) {
return downloadReleaseData(minioReleaseURL+"minio.shasum", timeout)
}
current, err := getCurrentMinioVersion()
if err != nil {
errMsg = "Unable to fetch the current version of Minio server."
return
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
}
// Initialize new request.
req, err := http.NewRequest("GET", newUpdateURL, nil)
if err != nil {
return
releaseInfo := fields[1]
if fields = strings.Split(releaseInfo, "."); len(fields) != 3 {
err = fmt.Errorf("Unknown release information `%s`", releaseInfo)
return releaseTime, err
}
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) "
if !(fields[0] == "minio" && fields[1] == "RELEASE") {
err = fmt.Errorf("Unknown release '%s'", releaseInfo)
return releaseTime, err
}
return prefix
} // else { not source.
if isDocker() {
prefix = prefix + "; " + "docker) "
} else {
prefix = prefix + ") "
releaseTime, err = time.Parse(releaseTagTimeLayout, fields[2])
if err != nil {
err = fmt.Errorf("Unknown release time format. %s", err)
}
return prefix
}()
// Set user agent.
req.Header.Set("User-Agent", userAgentPrefix+" "+userAgentSuffix)
return releaseTime, err
}
// Fetch new update.
resp, err := client.Do(req)
func getLatestReleaseTime(timeout time.Duration) (releaseTime time.Time, err error) {
data, err := DownloadReleaseData(timeout)
if err != nil {
return
return releaseTime, err
}
// 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
return parseReleaseData(data)
}
func getDownloadURL() (downloadURL string) {
if IsDocker() {
return "docker pull minio/minio"
}
// Read the response body.
updateBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
errMsg = "Failed to retrieve update notice. Please try again later."
return
if runtime.GOOS == "windows" {
return minioReleaseURL + "minio.exe"
}
errMsg = "Failed to retrieve update notice. Please try again later. Please report this issue at https://github.com/minio/minio/issues"
return minioReleaseURL + "minio"
}
// Parse the date if its valid.
latest, err := parseReleaseData(string(updateBody))
func getUpdateInfo(timeout time.Duration) (older time.Duration, downloadURL string, err error) {
currentReleaseTime, err := GetCurrentReleaseTime()
if err != nil {
return
return older, downloadURL, err
}
// 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
latestReleaseTime, err := getLatestReleaseTime(timeout)
if err != nil {
return older, downloadURL, err
}
// Is the update latest?.
if latest.After(current) {
updateMsg.Update = true
updateMsg.NewerThan = latest.Sub(current)
if latestReleaseTime.After(currentReleaseTime) {
older = latestReleaseTime.Sub(currentReleaseTime)
downloadURL = getDownloadURL()
}
// Return update message.
return updateMsg, "", nil
return older, downloadURL, nil
}
// main entry point for update command.
func mainUpdate(ctx *cli.Context) {
// Initialization routine, such as config loading, enable logging, ..
minioInit(ctx)
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...)
}
}
older, downloadURL, err := getUpdateInfo(10 * time.Second)
if err != nil {
quietPrintln(err)
os.Exit(-1)
}
if globalQuiet {
return
if older != time.Duration(0) {
quietPrintln(colorizeUpdateMessage(downloadURL, older))
os.Exit(1)
}
// 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…
Cancel
Save