-
Notifications
You must be signed in to change notification settings - Fork 26
/
util.go
213 lines (177 loc) · 6.29 KB
/
util.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
package util
import (
"flag"
"fmt"
"log"
"os"
"strconv"
"strings"
"time"
)
// Verbose mode active?
var Verbose = false
//BufferQCapacity number of pre-allocated buffers
const BufferQCapacity = 50
//LargeBlockSizeMax maximum block size
const LargeBlockSizeMax = 100 * MB
//LargeBlockAPIVersion API version that supports large block blobs
const LargeBlockAPIVersion = "2018-03-28"
//MiByte bytes in one MiB
const MiByte = 1048576
//MaxBlockCount the maximum number of blocks in a blob
const MaxBlockCount = 50000 // no more than this many blob blocks permitted
///////////////////////////////////////////////////////////////////
// Storage sizes -- print and scan bytes, and sizes suffixed with KB,MB,GB,TB
//////////////////////////////////////////////////////////////////
const (
KB = uint64(1024)
MB = 1024 * KB
GB = 1024 * MB
TB = 1024 * GB
KBF = float64(KB)
MBF = float64(MB)
GBF = float64(GB)
TBF = float64(TB)
)
//PrintSize formats a string with a compact respresentation of a byte count
func PrintSize(bytes uint64) string {
var str = "0"
var zeroTrim = true
var suffix = "GiB"
if bytes >= GB { // most common case
str = fmt.Sprintf("%0.1f", float64(bytes)/GBF) //TODO: rounding
} else if bytes < KB {
str = fmt.Sprintf("%d", bytes)
suffix = "B"
} else if bytes < MB {
str = fmt.Sprintf("%0.1f", float64(bytes)/KBF)
suffix = "KiB"
} else { // what's left is the MB range {
str = fmt.Sprintf("%0.1f", float64(bytes)/MBF)
suffix = "MiB"
}
if zeroTrim {
if strings.HasSuffix(str, ".0") {
str = str[:len(str)-2] // drop it
}
}
return str + suffix + fmt.Sprintf(" (%v)", bytes)
}
// ByteCountFromSizeString accepts byte count, or integer suffixed with B, KB, MB, GB.
// ... Return the corresponding count of bytes.
func ByteCountFromSizeString(sizeStr string) (uint64, error) {
sstr := strings.TrimSpace(sizeStr)
var scaler uint64 = 1
suffixCount := 2
if strings.HasSuffix(sstr, "GB") {
scaler = 1024 * 1024 * 1024
} else if strings.HasSuffix(sstr, "MB") {
scaler = 1024 * 1024
} else if strings.HasSuffix(sstr, "KB") {
scaler = 1024
} else if strings.HasSuffix(sstr, "B") {
suffixCount = 1
} else {
suffixCount = 0 // bare integer, no suffix
}
sstr = sstr[:len(sstr)-suffixCount] // drop suffix, if any
res, err := strconv.ParseUint(sstr, 10, 64)
if err != nil {
res = 0
}
res = res * scaler
return res, err
}
///////////////////////////////////////////////////////////////////
// Flags-related Helpers -- processing command line options
///////////////////////////////////////////////////////////////////
// TODO: really should be processed according to Linux conventions where one character flags have a single dash and longer names have 2.
// ... e.g. -n vs. --name_space
// ... Go's flags package lets you define '--xyz' as an option, but fails to parse it correctly. So current behavior is that -x and
// ... --x are treated equivalently. This allows for the case of normal Linux conventions, but doesn't enforce it.
//StringVarAlias string commandline option
func StringVarAlias(varPtr *string, shortflag string, longflag string, defaultVal string, description string) {
flag.StringVar(varPtr, shortflag, defaultVal, description)
flag.StringVar(varPtr, longflag, defaultVal, "")
}
//PrintUsageDefaults print commandline usage options
func PrintUsageDefaults(shortflag string, longflag string, defaultVal string, description string) {
defaultMsg := ""
if defaultVal != "" {
defaultMsg = fmt.Sprintf("\n\tDefault value: %v", defaultVal)
}
fmt.Fprintln(os.Stderr, fmt.Sprintf("-%v, --%v :\n\t%v%v", shortflag, longflag, description, defaultMsg))
}
//ListFlag represents a multivalue flag
type ListFlag []string
//String joints all values of the flag
func (lst *ListFlag) String() string {
return strings.Join(*lst, " ")
}
//Set adds a new value to the values list
func (lst *ListFlag) Set(value string) error {
*lst = append(*lst, value)
return nil
}
//StringListVarAlias string commandline option
func StringListVarAlias(varPtr *ListFlag, shortflag string, longflag string, defaultVal string, description string) {
flag.Var(varPtr, shortflag, description)
flag.Var(varPtr, longflag, "")
}
//IntVarAlias int commandline option
func IntVarAlias(varPtr *int, shortflag string, longflag string, defaultVal int, description string) {
flag.IntVar(varPtr, shortflag, defaultVal, description)
flag.IntVar(varPtr, longflag, defaultVal, "")
}
//Uint64VarAlias uint64 commandline option
func Uint64VarAlias(varPtr *uint64, shortflag string, longflag string, defaultVal uint64, description string) {
flag.Uint64Var(varPtr, shortflag, defaultVal, description)
flag.Uint64Var(varPtr, longflag, defaultVal, "")
}
//BoolVarAlias bool commandline option
func BoolVarAlias(varPtr *bool, shortflag string, longflag string, defaultVal bool, description string) {
flag.BoolVar(varPtr, shortflag, defaultVal, description)
flag.BoolVar(varPtr, longflag, defaultVal, "")
}
///////////////////////////////////////////////////////////////////
// Retriable execution of a function
///////////////////////////////////////////////////////////////////
const retryLimit = 100 // max retries for an operation in retriableOperation
const retrySleepDuration = time.Millisecond * 200 // Retry wait interval in retriableOperation
//RetriableOperation executes a function, retrying up to "retryLimit" times and waiting "retrySleepDuration" between attempts
func RetriableOperation(operation func(r int) error) (duration time.Duration, startTime time.Time, numOfRetries int) {
var err error
var retries int
t0 := time.Now()
for {
if retries >= retryLimit {
fmt.Print("Max number of retries was exceeded.\n")
if !Verbose {
handleExceededRetries(err)
} else {
panic(err)
}
}
if err = operation(retries); err == nil {
t1 := time.Now()
duration = t1.Sub(t0)
startTime = t1
numOfRetries = retries
return
}
retries++
PrintfIfDebug("RetriableOperation -> |%v|%v", retries, err)
time.Sleep(retrySleepDuration)
}
}
func handleExceededRetries(err error) {
errMsg := fmt.Sprintf("The number of retries has exceeded the maximum allowed.\nError: %v\n", err.Error())
log.Fatal(errMsg)
}
//PrintfIfDebug TODO
func PrintfIfDebug(format string, values ...interface{}) {
if Verbose {
msg := fmt.Sprintf(format, values...)
fmt.Printf("%v\t%s\n", time.Now(), msg)
}
}