github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/toolkit/internal/common/common.go (about) 1 package common 2 3 // 4 // gopsutil is a port of psutil(http://pythonhosted.org/psutil/). 5 // This covers these architectures. 6 // - linux (amd64, arm) 7 // - freebsd (amd64) 8 // - windows (amd64) 9 import ( 10 "bufio" 11 "bytes" 12 "context" 13 "errors" 14 "fmt" 15 "io" 16 "io/ioutil" 17 "net/url" 18 "os" 19 "os/exec" 20 "path" 21 "path/filepath" 22 "reflect" 23 "runtime" 24 "strconv" 25 "strings" 26 "time" 27 ) 28 29 var ( 30 Timeout = 3 * time.Second 31 ErrTimeout = errors.New("command timed out") 32 ) 33 34 type Invoker interface { 35 Command(string, ...string) ([]byte, error) 36 CommandWithContext(context.Context, string, ...string) ([]byte, error) 37 } 38 39 type Invoke struct{} 40 41 func (i Invoke) Command(name string, arg ...string) ([]byte, error) { 42 ctx, cancel := context.WithTimeout(context.Background(), Timeout) 43 defer cancel() 44 return i.CommandWithContext(ctx, name, arg...) 45 } 46 47 func (i Invoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) { 48 cmd := exec.CommandContext(ctx, name, arg...) 49 50 var buf bytes.Buffer 51 cmd.Stdout = &buf 52 cmd.Stderr = &buf 53 54 if err := cmd.Start(); err != nil { 55 return buf.Bytes(), err 56 } 57 58 if err := cmd.Wait(); err != nil { 59 return buf.Bytes(), err 60 } 61 62 return buf.Bytes(), nil 63 } 64 65 type FakeInvoke struct { 66 Suffix string // Suffix species expected file name suffix such as "fail" 67 Error error // If Error specified, return the error. 68 } 69 70 // Command in FakeInvoke returns from expected file if exists. 71 func (i FakeInvoke) Command(name string, arg ...string) ([]byte, error) { 72 if i.Error != nil { 73 return []byte{}, i.Error 74 } 75 76 arch := runtime.GOOS 77 78 commandName := filepath.Base(name) 79 80 fname := strings.Join(append([]string{commandName}, arg...), "") 81 fname = url.QueryEscape(fname) 82 fpath := path.Join("testdata", arch, fname) 83 if i.Suffix != "" { 84 fpath += "_" + i.Suffix 85 } 86 if PathExists(fpath) { 87 return ioutil.ReadFile(fpath) 88 } 89 return []byte{}, fmt.Errorf("could not find testdata: %s", fpath) 90 } 91 92 func (i FakeInvoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) { 93 return i.Command(name, arg...) 94 } 95 96 var ErrNotImplementedError = errors.New("not implemented yet") 97 98 // ReadFile reads contents from a file 99 func ReadFile(filename string) (string, error) { 100 content, err := ioutil.ReadFile(filename) 101 if err != nil { 102 return "", err 103 } 104 105 return string(content), nil 106 } 107 108 // ReadLines reads contents from a file and splits them by new lines. 109 // A convenience wrapper to ReadLinesOffsetN(filename, 0, -1). 110 func ReadLines(filename string) ([]string, error) { 111 return ReadLinesOffsetN(filename, 0, -1) 112 } 113 114 // ReadLinesOffsetN reads contents from file and splits them by new line. 115 // The offset tells at which line number to start. 116 // The count determines the number of lines to read (starting from offset): 117 // n >= 0: at most n lines 118 // n < 0: whole file 119 func ReadLinesOffsetN(filename string, offset uint, n int) ([]string, error) { 120 f, err := os.Open(filename) 121 if err != nil { 122 return []string{""}, err 123 } 124 defer f.Close() 125 126 var ret []string 127 128 r := bufio.NewReader(f) 129 for i := 0; i < n+int(offset) || n < 0; i++ { 130 line, err := r.ReadString('\n') 131 if err != nil { 132 if err == io.EOF && len(line) > 0 { 133 ret = append(ret, strings.Trim(line, "\n")) 134 } 135 break 136 } 137 if i < int(offset) { 138 continue 139 } 140 ret = append(ret, strings.Trim(line, "\n")) 141 } 142 143 return ret, nil 144 } 145 146 func IntToString(orig []int8) string { 147 ret := make([]byte, len(orig)) 148 size := -1 149 for i, o := range orig { 150 if o == 0 { 151 size = i 152 break 153 } 154 ret[i] = byte(o) 155 } 156 if size == -1 { 157 size = len(orig) 158 } 159 160 return string(ret[0:size]) 161 } 162 163 func UintToString(orig []uint8) string { 164 ret := make([]byte, len(orig)) 165 size := -1 166 for i, o := range orig { 167 if o == 0 { 168 size = i 169 break 170 } 171 ret[i] = byte(o) 172 } 173 if size == -1 { 174 size = len(orig) 175 } 176 177 return string(ret[0:size]) 178 } 179 180 func ByteToString(orig []byte) string { 181 n := -1 182 l := -1 183 for i, b := range orig { 184 // skip left side null 185 if l == -1 && b == 0 { 186 continue 187 } 188 if l == -1 { 189 l = i 190 } 191 192 if b == 0 { 193 break 194 } 195 n = i + 1 196 } 197 if n == -1 { 198 return string(orig) 199 } 200 return string(orig[l:n]) 201 } 202 203 // ReadInts reads contents from single line file and returns them as []int32. 204 func ReadInts(filename string) ([]int64, error) { 205 f, err := os.Open(filename) 206 if err != nil { 207 return []int64{}, err 208 } 209 defer f.Close() 210 211 var ret []int64 212 213 r := bufio.NewReader(f) 214 215 // The int files that this is concerned with should only be one liners. 216 line, err := r.ReadString('\n') 217 if err != nil { 218 return []int64{}, err 219 } 220 221 i, err := strconv.ParseInt(strings.Trim(line, "\n"), 10, 32) 222 if err != nil { 223 return []int64{}, err 224 } 225 ret = append(ret, i) 226 227 return ret, nil 228 } 229 230 // Parse Hex to uint32 without error 231 func HexToUint32(hex string) uint32 { 232 vv, _ := strconv.ParseUint(hex, 16, 32) 233 return uint32(vv) 234 } 235 236 // Parse to int32 without error 237 func mustParseInt32(val string) int32 { 238 vv, _ := strconv.ParseInt(val, 10, 32) 239 return int32(vv) 240 } 241 242 // Parse to uint64 without error 243 func mustParseUint64(val string) uint64 { 244 vv, _ := strconv.ParseInt(val, 10, 64) 245 return uint64(vv) 246 } 247 248 // Parse to Float64 without error 249 func mustParseFloat64(val string) float64 { 250 vv, _ := strconv.ParseFloat(val, 64) 251 return vv 252 } 253 254 // StringsHas checks the target string slice contains src or not 255 func StringsHas(target []string, src string) bool { 256 for _, t := range target { 257 if strings.TrimSpace(t) == src { 258 return true 259 } 260 } 261 return false 262 } 263 264 // StringsContains checks the src in any string of the target string slice 265 func StringsContains(target []string, src string) bool { 266 for _, t := range target { 267 if strings.Contains(t, src) { 268 return true 269 } 270 } 271 return false 272 } 273 274 // IntContains checks the src in any int of the target int slice. 275 func IntContains(target []int, src int) bool { 276 for _, t := range target { 277 if src == t { 278 return true 279 } 280 } 281 return false 282 } 283 284 // get struct attributes. 285 // This method is used only for debugging platform dependent code. 286 func attributes(m interface{}) map[string]reflect.Type { 287 typ := reflect.TypeOf(m) 288 if typ.Kind() == reflect.Ptr { 289 typ = typ.Elem() 290 } 291 292 attrs := make(map[string]reflect.Type) 293 if typ.Kind() != reflect.Struct { 294 return nil 295 } 296 297 for i := 0; i < typ.NumField(); i++ { 298 p := typ.Field(i) 299 if !p.Anonymous { 300 attrs[p.Name] = p.Type 301 } 302 } 303 304 return attrs 305 } 306 307 func PathExists(filename string) bool { 308 if _, err := os.Stat(filename); err == nil { 309 return true 310 } 311 return false 312 } 313 314 // PathExistsWithContents returns the filename exists and it is not empty 315 func PathExistsWithContents(filename string) bool { 316 info, err := os.Stat(filename) 317 if err != nil { 318 return false 319 } 320 return info.Size() > 4 // at least 4 bytes 321 } 322 323 // GetEnv retrieves the environment variable key. If it does not exist it returns the default. 324 func GetEnv(key string, dfault string, combineWith ...string) string { 325 value := os.Getenv(key) 326 if value == "" { 327 value = dfault 328 } 329 330 switch len(combineWith) { 331 case 0: 332 return value 333 case 1: 334 return filepath.Join(value, combineWith[0]) 335 default: 336 all := make([]string, len(combineWith)+1) 337 all[0] = value 338 copy(all[1:], combineWith) 339 return filepath.Join(all...) 340 } 341 } 342 343 func HostProc(combineWith ...string) string { 344 return GetEnv("HOST_PROC", "/proc", combineWith...) 345 } 346 347 func HostSys(combineWith ...string) string { 348 return GetEnv("HOST_SYS", "/sys", combineWith...) 349 } 350 351 func HostEtc(combineWith ...string) string { 352 return GetEnv("HOST_ETC", "/etc", combineWith...) 353 } 354 355 func HostVar(combineWith ...string) string { 356 return GetEnv("HOST_VAR", "/var", combineWith...) 357 } 358 359 func HostRun(combineWith ...string) string { 360 return GetEnv("HOST_RUN", "/run", combineWith...) 361 } 362 363 func HostDev(combineWith ...string) string { 364 return GetEnv("HOST_DEV", "/dev", combineWith...) 365 } 366 367 // MockEnv set environment variable and return revert function. 368 // MockEnv should be used testing only. 369 func MockEnv(key string, value string) func() { 370 original := os.Getenv(key) 371 os.Setenv(key, value) 372 return func() { 373 os.Setenv(key, original) 374 } 375 } 376 377 // getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running 378 // sysctl commands (see DoSysctrl). 379 func getSysctrlEnv(env []string) []string { 380 foundLC := false 381 for i, line := range env { 382 if strings.HasPrefix(line, "LC_ALL") { 383 env[i] = "LC_ALL=C" 384 foundLC = true 385 } 386 } 387 if !foundLC { 388 env = append(env, "LC_ALL=C") 389 } 390 return env 391 }