-
Notifications
You must be signed in to change notification settings - Fork 100
/
update.go
308 lines (261 loc) · 7.27 KB
/
update.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
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,
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)
}
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
if runtime.GOARCH == "386" {
object = updateBinaryMac32
}
case "windows":
object = updateBinaryWindow64
if runtime.GOARCH == "386" {
object = updateBinaryWindow32
}
default:
object = updateBinaryLinux64
if runtime.GOARCH == "386" {
object = updateBinaryLinux32
}
}
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
}