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  }