mirror of
				https://github.com/minio/minio.git
				synced 2025-11-04 02:01:05 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			237 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						||
 * 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 cmd
 | 
						||
 | 
						||
import (
 | 
						||
	"encoding/json"
 | 
						||
	"errors"
 | 
						||
	"io/ioutil"
 | 
						||
	"net/http"
 | 
						||
	"runtime"
 | 
						||
	"strings"
 | 
						||
	"time"
 | 
						||
 | 
						||
	"github.com/fatih/color"
 | 
						||
	"github.com/minio/cli"
 | 
						||
	"github.com/minio/mc/pkg/console"
 | 
						||
)
 | 
						||
 | 
						||
// command specific flags.
 | 
						||
var (
 | 
						||
	updateFlags = []cli.Flag{
 | 
						||
		cli.BoolFlag{
 | 
						||
			Name:  "experimental, E",
 | 
						||
			Usage: "Check experimental update.",
 | 
						||
		},
 | 
						||
	}
 | 
						||
)
 | 
						||
 | 
						||
// Check for new software updates.
 | 
						||
var updateCmd = cli.Command{
 | 
						||
	Name:   "update",
 | 
						||
	Usage:  "Check for a new software update.",
 | 
						||
	Action: mainUpdate,
 | 
						||
	Flags:  append(updateFlags, globalFlags...),
 | 
						||
	CustomHelpTemplate: `Name:
 | 
						||
   minio {{.Name}} - {{.Usage}}
 | 
						||
 | 
						||
USAGE:
 | 
						||
   minio {{.Name}} [FLAGS]
 | 
						||
 | 
						||
FLAGS:
 | 
						||
  {{range .Flags}}{{.}}
 | 
						||
  {{end}}
 | 
						||
EXAMPLES:
 | 
						||
   1. Check for any new official release.
 | 
						||
      $ minio {{.Name}}
 | 
						||
 | 
						||
   2. Check for any new experimental release.
 | 
						||
      $ minio {{.Name}} --experimental
 | 
						||
`,
 | 
						||
}
 | 
						||
 | 
						||
// update URL endpoints.
 | 
						||
const (
 | 
						||
	minioUpdateStableURL       = "https://dl.minio.io/server/minio/release"
 | 
						||
	minioUpdateExperimentalURL = "https://dl.minio.io/server/minio/experimental"
 | 
						||
)
 | 
						||
 | 
						||
// updateMessage container to hold update messages.
 | 
						||
type updateMessage struct {
 | 
						||
	Status   string `json:"status"`
 | 
						||
	Update   bool   `json:"update"`
 | 
						||
	Download string `json:"downloadURL"`
 | 
						||
	Version  string `json:"version"`
 | 
						||
}
 | 
						||
 | 
						||
// 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’.")
 | 
						||
	}
 | 
						||
	msg := colorizeUpdateMessage(u.Download)
 | 
						||
	return msg
 | 
						||
}
 | 
						||
 | 
						||
// JSON jsonified update message.
 | 
						||
func (u updateMessage) JSON() string {
 | 
						||
	u.Status = "success"
 | 
						||
	updateMessageJSONBytes, err := json.Marshal(u)
 | 
						||
	fatalIf((err), "Unable to marshal into JSON.")
 | 
						||
 | 
						||
	return string(updateMessageJSONBytes)
 | 
						||
}
 | 
						||
 | 
						||
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] != "minio" {
 | 
						||
		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)
 | 
						||
	if err != nil {
 | 
						||
		return time.Time{}, err
 | 
						||
	}
 | 
						||
	return parsedDate, nil
 | 
						||
}
 | 
						||
 | 
						||
// verify updates for releases.
 | 
						||
func getReleaseUpdate(updateURL string) (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
 | 
						||
	switch runtime.GOOS {
 | 
						||
	case "windows":
 | 
						||
		// For windows.
 | 
						||
		downloadURL = newUpdateURLPrefix + "/minio.exe"
 | 
						||
	default:
 | 
						||
		// For all other operating systems.
 | 
						||
		downloadURL = newUpdateURLPrefix + "/minio"
 | 
						||
	}
 | 
						||
 | 
						||
	// Initialize update message.
 | 
						||
	updateMsg = updateMessage{
 | 
						||
		Download: downloadURL,
 | 
						||
		Version:  Version,
 | 
						||
	}
 | 
						||
 | 
						||
	// Instantiate a new client with 3 sec timeout.
 | 
						||
	client := &http.Client{
 | 
						||
		Timeout: 3 * time.Second,
 | 
						||
	}
 | 
						||
 | 
						||
	// Parse current minio version into RFC3339.
 | 
						||
	current, err := time.Parse(time.RFC3339, Version)
 | 
						||
	if err != nil {
 | 
						||
		errMsg = "Unable to parse version string as time."
 | 
						||
		return
 | 
						||
	}
 | 
						||
 | 
						||
	// Verify if current minio version is zero.
 | 
						||
	if current.IsZero() {
 | 
						||
		err = errors.New("date should not be zero")
 | 
						||
		errMsg = "Updates mechanism is not supported for custom builds. Please download official releases from https://minio.io/#minio"
 | 
						||
		return
 | 
						||
	}
 | 
						||
 | 
						||
	// Fetch new update.
 | 
						||
	data, err := client.Get(newUpdateURL)
 | 
						||
	if err != nil {
 | 
						||
		return
 | 
						||
	}
 | 
						||
 | 
						||
	// Verify if we have a valid http response i.e http.StatusOK.
 | 
						||
	if data != nil {
 | 
						||
		if data.StatusCode != http.StatusOK {
 | 
						||
			errMsg = "Failed to retrieve update notice."
 | 
						||
			err = errors.New("http status : " + data.Status)
 | 
						||
			return
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// Read the response body.
 | 
						||
	updateBody, err := ioutil.ReadAll(data.Body)
 | 
						||
	if err != nil {
 | 
						||
		errMsg = "Failed to retrieve update notice. Please try again later."
 | 
						||
		return
 | 
						||
	}
 | 
						||
 | 
						||
	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
 | 
						||
	}
 | 
						||
 | 
						||
	// Verify if the date is not zero.
 | 
						||
	if latest.IsZero() {
 | 
						||
		err = errors.New("date should not be zero")
 | 
						||
		return
 | 
						||
	}
 | 
						||
 | 
						||
	// Is the update latest?.
 | 
						||
	if latest.After(current) {
 | 
						||
		updateMsg.Update = true
 | 
						||
	}
 | 
						||
 | 
						||
	// Return update message.
 | 
						||
	return updateMsg, "", nil
 | 
						||
}
 | 
						||
 | 
						||
// main entry point for update command.
 | 
						||
func mainUpdate(ctx *cli.Context) {
 | 
						||
	// Error out if 'update' command is issued for development based builds.
 | 
						||
	if Version == "DEVELOPMENT.GOGET" {
 | 
						||
		fatalIf(errors.New(""), "Update mechanism is not supported for ‘go get’ based binary builds. Please download official releases from https://minio.io/#minio")
 | 
						||
	}
 | 
						||
 | 
						||
	// Check for update.
 | 
						||
	var updateMsg updateMessage
 | 
						||
	var errMsg string
 | 
						||
	var err error
 | 
						||
	if ctx.Bool("experimental") {
 | 
						||
		updateMsg, errMsg, err = getReleaseUpdate(minioUpdateExperimentalURL)
 | 
						||
	} else {
 | 
						||
		updateMsg, errMsg, err = getReleaseUpdate(minioUpdateStableURL)
 | 
						||
	}
 | 
						||
	fatalIf(err, errMsg)
 | 
						||
	console.Println(updateMsg)
 | 
						||
}
 |