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 }