oss/lib/update.go (261 lines of code) (raw):

package lib import ( "fmt" "io" "io/ioutil" "net/http" "os" "runtime" "sort" "strings" ) var ( vUpdateEndpoint = updateEndpoint vUpdateBucket = updateBucket vVersion = Version ) var specChineseUpdate = SpecText{ synopsisText: "更新ossutil", paramText: "[options]", syntaxText: ` ossutil update [-f] `, detailHelpText: ` 该命令检查当前ossutil的版本与最新版本,输出两者的版本号,如果有更新版本,询问是否 进行升级。如果指定了--force选项,则不询问,当有可用更新时,直接升级。 `, sampleText: ` ossutil update ossutil update -f `, } var specEnglishUpdate = SpecText{ synopsisText: "Update ossutil", paramText: "[options]", syntaxText: ` ossutil update [-f] `, detailHelpText: ` The command check version of current ossutil and get the latest version, output the versions, if any updated version exists, the command ask you for upgrading. If --force option is specified, the command upgrade without asking. `, sampleText: ` ossutil update ossutil update -f `, } // UpdateCommand is the command update ossutil type UpdateCommand struct { command Command } var updateCommand = UpdateCommand{ command: Command{ name: "update", nameAlias: []string{""}, minArgc: 0, maxArgc: 0, specChinese: specChineseUpdate, specEnglish: specEnglishUpdate, group: GroupTypeAdditionalCommand, validOptionNames: []string{ OptionForce, OptionRetryTimes, OptionLanguage, OptionProxyHost, OptionProxyUser, OptionProxyPwd, OptionLogLevel, }, }, } // function for RewriteLoadConfiger interface func (uc *UpdateCommand) rewriteLoadConfig(configFile string) error { // read config file, if error exist, do not print error var err error if uc.command.configOptions, err = LoadConfig(configFile); err != nil { uc.command.configOptions = OptionMapType{} } return nil } // function for FormatHelper interface func (uc *UpdateCommand) formatHelpForWhole() string { return uc.command.formatHelpForWhole() } func (uc *UpdateCommand) formatIndependHelp() string { return uc.command.formatIndependHelp() } // Init simulate inheritance, and polymorphism func (uc *UpdateCommand) Init(args []string, options OptionMapType) error { return uc.command.Init(args, options, uc) } // RunCommand simulate inheritance, and polymorphism func (uc *UpdateCommand) RunCommand() error { force, _ := GetBool(OptionForce, uc.command.options) language, _ := GetString(OptionLanguage, uc.command.options) language = strings.ToLower(language) // get lastest version version, err := uc.getLastestVersion() if err != nil { return fmt.Errorf("get lastest vsersion error, %s", err.Error()) } if language == LEnglishLanguage { fmt.Printf("current version is: %s, the lastest version is: %s", vVersion, version) } else { fmt.Printf("当前版本为:%s,最新版本为:%s", vVersion, version) } // version is X.X.X // vVersion is vX.X.X if vVersion[0] < '0' || vVersion[0] > '9' { vVersion = vVersion[1:len(vVersion)] } if version == vVersion { if language == LEnglishLanguage { fmt.Println(", current version is the lastest version, no need to update.") } else { fmt.Println(",当前版本即为最新版本,无需更新。") } return nil } fmt.Println("") if !force { if language == LEnglishLanguage { fmt.Printf("sure to update ossutil(y or N)? ") } else { fmt.Printf("确定更新版本(y or N)? ") } var val string if _, err := fmt.Scanln(&val); err == nil && (strings.EqualFold(val, "yes") || strings.EqualFold(val, "y")) { return uc.updateVersion(version, language) } if language == LEnglishLanguage { fmt.Printf("operation is canceled.") } else { fmt.Println("操作取消。") } } else { return uc.updateVersion(version, language) } return nil } func (uc *UpdateCommand) getLastestVersion() (string, error) { if err := uc.anonymousGetToFileRetry(vUpdateBucket, updateVersionObject, updateTmpVersionFile); err != nil { return "", err } v, err := ioutil.ReadFile(updateTmpVersionFile) if err != nil { return "", err } versionStr := strings.TrimSpace(strings.Trim(string(v), "\n")) // get version list and sort sli := strings.Split(versionStr, "\n") vl := []string{} for _, vstr := range sli { vl = append(vl, strings.TrimSpace(strings.Trim(string(vstr), "\n"))) } sort.Strings(vl) version := vl[len(vl)-1] os.Remove(updateTmpVersionFile) return version, nil } func (uc *UpdateCommand) anonymousGetToFileRetry(bucketName, objectName, filePath string) error { host := fmt.Sprintf("http://%s.%s/%s", bucketName, vUpdateEndpoint, objectName) retryTimes, _ := GetInt(OptionRetryTimes, uc.command.options) for i := 1; ; i++ { err := uc.ossAnonymousGetToFile(host, filePath) if err == nil { return err } if int64(i) >= retryTimes { return ObjectError{err, bucketName, objectName} } } } func (uc *UpdateCommand) ossAnonymousGetToFile(host, filePath string) error { response, err := http.Get(host) if err != nil { return err } defer response.Body.Close() statusCode := response.StatusCode body, _ := ioutil.ReadAll(response.Body) if statusCode >= 300 { return fmt.Errorf(string(body)) } fd, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640) defer fd.Close() if err != nil { return err } _, err = io.WriteString(fd, string(body)) if err != nil { return err } return nil } func (uc *UpdateCommand) updateVersion(version, language string) error { // get binary path filePath, renameFilePath := getBinaryPath() // get binary mode f, err := os.Stat(filePath) if err != nil { return err } mode := f.Mode() // rename the current binary to another one if err := os.Rename(filePath, renameFilePath); err != nil { return fmt.Errorf("update binary error, %s", err.Error()) } // download the binary of the specified version if err := uc.getBinary(filePath, version); err != nil { uc.revertRename(filePath, renameFilePath) return fmt.Errorf("download binary of version: %s error, %s", version, err.Error()) } if err := os.Chmod(filePath, mode); err != nil { uc.revertRename(filePath, renameFilePath) return fmt.Errorf("chmod binary error, %s", err.Error()) } // remove the current one if runtime.GOOS != "windows" { if err := os.Remove(renameFilePath); err != nil { uc.revertRename(filePath, renameFilePath) return fmt.Errorf("remove old binary error, %s", err.Error()) } } if language == LEnglishLanguage { fmt.Println("Update Success!") } else { fmt.Println("更新成功!") } return nil } func (uc *UpdateCommand) revertRename(filePath, renameFilePath string) error { if _, err := os.Stat(filePath); err == nil { os.Remove(filePath) } if err := os.Rename(renameFilePath, filePath); err != nil { return err } return nil } func (uc *UpdateCommand) getBinaryName() string { // get os type var object string switch runtime.GOOS { case "darwin": object = updateBinaryMac64 switch runtime.GOARCH { case "386": object = updateBinaryMac32 case "arm64": object = updateBinaryMacArm64 } case "windows": object = updateBinaryWindow64 if runtime.GOARCH == "386" { object = updateBinaryWindow32 } default: object = updateBinaryLinux64 switch runtime.GOARCH { case "386": object = updateBinaryLinux32 case "arm": object = updateBinaryLinuxArm32 case "arm64": object = updateBinaryLinuxArm64 } } return object } func (uc *UpdateCommand) getBinary(filePath, version string) error { object := version + "/" + uc.getBinaryName() if err := uc.anonymousGetToFileRetry(vUpdateBucket, object, filePath); err != nil { return err } return nil }