github.com/projecteru2/core@v0.0.0-20240321043226-06bcc1c23f58/utils/utils.go (about)

     1  package utils
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/rand"
     7  	"crypto/sha256"
     8  	"encoding/hex"
     9  	"encoding/json"
    10  	"fmt"
    11  	"io"
    12  	"math"
    13  	"math/big"
    14  	"os"
    15  	"strings"
    16  	"time"
    17  	"unsafe"
    18  
    19  	"github.com/cockroachdb/errors"
    20  	"github.com/projecteru2/core/cluster"
    21  	"github.com/projecteru2/core/log"
    22  	"github.com/projecteru2/core/types"
    23  )
    24  
    25  const (
    26  	letters       = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    27  	shortenLength = 7
    28  	// DefaultVersion for default version
    29  	DefaultVersion = "latest"
    30  )
    31  
    32  // RandomString random a string
    33  func RandomString(n int) string {
    34  	r := make([]byte, n)
    35  	for i := 0; i < n; i++ {
    36  		n, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
    37  		// 没那么惨吧
    38  		if err != nil {
    39  			continue
    40  		}
    41  		r[i] = letters[n.Int64()]
    42  	}
    43  	return string(r)
    44  }
    45  
    46  // Tail return tail thing
    47  func Tail(path string) string {
    48  	return path[strings.LastIndex(path, "/")+1:]
    49  }
    50  
    51  // GetGitRepoName return git repo name
    52  func GetGitRepoName(url string) (string, error) {
    53  	if !(strings.Contains(url, "git@") || strings.Contains(url, "gitlab@") || strings.Contains(url, "https://")) || !strings.HasSuffix(url, ".git") {
    54  		return "", errors.Wrap(types.ErrInvalidGitURL, url)
    55  	}
    56  
    57  	return strings.TrimSuffix(Tail(url), ".git"), nil
    58  }
    59  
    60  // GetTag reture image tag
    61  func GetTag(image string) string {
    62  	if !strings.Contains(image, ":") {
    63  		return DefaultVersion
    64  	}
    65  	return image[strings.LastIndex(image, ":")+1:]
    66  }
    67  
    68  // NormalizeImageName will normalize image name
    69  func NormalizeImageName(image string) string {
    70  	if !strings.Contains(image, ":") {
    71  		return fmt.Sprintf("%s:latest", image)
    72  	}
    73  	return image
    74  }
    75  
    76  // MakeCommandLineArgs make command line args
    77  func MakeCommandLineArgs(s string) []string {
    78  	r := []string{}
    79  	for _, part := range safeSplit(s) {
    80  		if len(part) == 0 {
    81  			continue
    82  		}
    83  		r = append(r, part)
    84  	}
    85  	return r
    86  }
    87  
    88  // MakeWorkloadName joins appname, entrypoint, ident using '_'
    89  func MakeWorkloadName(appname, entrypoint, ident string) string {
    90  	return strings.Join([]string{appname, entrypoint, ident}, "_")
    91  }
    92  
    93  // ParseWorkloadName does the opposite thing as MakeWorkloadName
    94  func ParseWorkloadName(workloadName string) (string, string, string, error) {
    95  	workloadName = strings.TrimLeft(workloadName, "/")
    96  	splits := strings.Split(workloadName, "_")
    97  	length := len(splits)
    98  	if length >= 3 {
    99  		return strings.Join(splits[0:length-2], "_"), splits[length-2], splits[length-1], nil
   100  	}
   101  	return "", "", "", errors.Wrap(types.ErrInvalidWorkloadName, workloadName)
   102  }
   103  
   104  // MakePublishInfo generate publish info
   105  func MakePublishInfo(networks map[string]string, ports []string) map[string][]string {
   106  	result := map[string][]string{}
   107  	for networkName, ip := range networks {
   108  		data := []string{}
   109  		for _, port := range ports {
   110  			data = append(data, fmt.Sprintf("%s:%s", ip, port))
   111  		}
   112  		if len(data) > 0 {
   113  			result[networkName] = data
   114  		}
   115  	}
   116  	return result
   117  }
   118  
   119  // EncodePublishInfo encode publish info
   120  func EncodePublishInfo(info map[string][]string) map[string]string {
   121  	result := map[string]string{}
   122  	for nm, publishs := range info {
   123  		if len(publishs) > 0 {
   124  			result[nm] = strings.Join(publishs, ",")
   125  		}
   126  	}
   127  	return result
   128  }
   129  
   130  // DecodePublishInfo decode publish info
   131  func DecodePublishInfo(info map[string]string) map[string][]string {
   132  	result := map[string][]string{}
   133  	for nm, publishs := range info {
   134  		if publishs != "" {
   135  			result[nm] = strings.Split(publishs, ",")
   136  		}
   137  	}
   138  	return result
   139  }
   140  
   141  // EncodeMetaInLabel encode meta to json
   142  func EncodeMetaInLabel(ctx context.Context, meta *types.LabelMeta) string {
   143  	data, err := json.Marshal(meta)
   144  	if err != nil {
   145  		log.WithFunc("utils.EncodeMetaInLabel").Error(ctx, err, "Encode meta failed")
   146  		return ""
   147  	}
   148  	return string(data)
   149  }
   150  
   151  // DecodeMetaInLabel get meta from label and decode it
   152  func DecodeMetaInLabel(ctx context.Context, labels map[string]string) *types.LabelMeta {
   153  	meta := &types.LabelMeta{}
   154  	metastr, ok := labels[cluster.LabelMeta]
   155  	if ok {
   156  		if err := json.Unmarshal([]byte(metastr), meta); err != nil {
   157  			log.WithFunc("utils.DecodeMetaInLabel").Error(ctx, err, "Decode failed")
   158  		}
   159  	}
   160  	return meta
   161  }
   162  
   163  // ShortID short workload ID
   164  func ShortID(workloadID string) string {
   165  	return workloadID[Max(0, len(workloadID)-shortenLength):]
   166  }
   167  
   168  // LabelsFilter filter workload by labels
   169  func LabelsFilter(extend map[string]string, labels map[string]string) bool {
   170  	for k, v := range labels {
   171  		if n, ok := extend[k]; !ok || n != v {
   172  			return false
   173  		}
   174  	}
   175  	return true
   176  }
   177  
   178  // CleanStatsdMetrics trans dot to _
   179  func CleanStatsdMetrics(k string) string {
   180  	return strings.ReplaceAll(k, ".", "-")
   181  }
   182  
   183  // TempFile store a temp file
   184  func TempFile(stream io.ReadCloser) (string, error) {
   185  	f, err := os.CreateTemp(os.TempDir(), "")
   186  	if err != nil {
   187  		return "", err
   188  	}
   189  	defer f.Close()
   190  	defer stream.Close()
   191  
   192  	_, err = io.Copy(f, stream)
   193  	return f.Name(), err
   194  }
   195  
   196  // Round for float64 to int
   197  func Round(f float64) float64 {
   198  	return math.Round(f*1000000000) / 1000000000
   199  }
   200  
   201  // MergeHookOutputs merge hooks output
   202  func MergeHookOutputs(outputs []*bytes.Buffer) []byte {
   203  	r := []byte{}
   204  	for _, m := range outputs {
   205  		r = append(r, m.Bytes()...)
   206  	}
   207  	return r
   208  }
   209  
   210  // EnsureReaderClosed As the name says,
   211  // blocks until the stream is empty, until we meet EOF
   212  func EnsureReaderClosed(ctx context.Context, stream io.ReadCloser) {
   213  	if stream == nil {
   214  		return
   215  	}
   216  	if _, err := io.Copy(io.Discard, stream); err != nil {
   217  		log.WithFunc("utils.EnsureReaderClosed").Error(ctx, err, "Empty stream failed")
   218  	}
   219  	_ = stream.Close()
   220  }
   221  
   222  // Range .
   223  func Range(n int) (res []int) {
   224  	for i := 0; i < n; i++ {
   225  		res = append(res, i)
   226  	}
   227  	return
   228  }
   229  
   230  // WithTimeout runs a function with given timeout
   231  func WithTimeout(ctx context.Context, timeout time.Duration, f func(context.Context)) {
   232  	ctx, cancel := context.WithTimeout(ctx, timeout)
   233  	defer cancel()
   234  	f(ctx)
   235  }
   236  
   237  // SHA256 .
   238  func SHA256(input string) string {
   239  	c := sha256.New()
   240  	c.Write([]byte(input))
   241  	bytes := c.Sum(nil)
   242  	return hex.EncodeToString(bytes)
   243  }
   244  
   245  // copied from https://gist.github.com/jmervine/d88c75329f98e09f5c87
   246  func safeSplit(s string) []string {
   247  	split := strings.Split(s, " ")
   248  
   249  	var result []string
   250  	var inquote string
   251  	var block string
   252  	for _, i := range split {
   253  		if inquote == "" {
   254  			if strings.HasPrefix(i, "'") || strings.HasPrefix(i, "\"") {
   255  				inquote = string(i[0])
   256  				block = strings.TrimPrefix(i, inquote) + " "
   257  			} else {
   258  				result = append(result, i)
   259  			}
   260  			continue
   261  		}
   262  		if !strings.HasSuffix(i, inquote) {
   263  			block += i + " "
   264  		} else {
   265  			block += strings.TrimSuffix(i, inquote)
   266  			inquote = ""
   267  			result = append(result, block)
   268  			block = ""
   269  		}
   270  	}
   271  
   272  	return result
   273  }
   274  
   275  func Bool2Int(a bool) int {
   276  	return *(*int)(unsafe.Pointer(&a)) & 1
   277  }