1// Copyright 2019 Istio Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package util
16
17import (
18	"bytes"
19	"fmt"
20	"html/template"
21	"io/ioutil"
22	"os"
23	"path/filepath"
24	"strconv"
25	"strings"
26)
27
28type FileFilter func(fileName string) bool
29
30// StringBoolMapToSlice creates and returns a slice of all the map keys with true.
31func StringBoolMapToSlice(m map[string]bool) []string {
32	s := make([]string, 0, len(m))
33	for k, v := range m {
34		if v {
35			s = append(s, k)
36		}
37	}
38	return s
39}
40
41// ReadFilesWithFilter reads files from path, for a directory it recursively reads files and filters the results
42// for single file it directly reads the file. It returns a concatenated output of all matching files' content.
43func ReadFilesWithFilter(path string, filter FileFilter) (string, error) {
44	fileList, err := FindFiles(path, filter)
45	if err != nil {
46		return "", err
47	}
48	var sb strings.Builder
49	for _, file := range fileList {
50		a, err := ioutil.ReadFile(file)
51		if err != nil {
52			return "", err
53		}
54		if _, err := sb.WriteString(string(a) + "\n"); err != nil {
55			return "", err
56		}
57	}
58	return sb.String(), nil
59}
60
61// FindFiles reads files from path, and returns the file names that match the filter.
62func FindFiles(path string, filter FileFilter) ([]string, error) {
63	fi, err := os.Stat(path)
64	if err != nil {
65		return nil, err
66	}
67	var fileList []string
68	if fi.IsDir() {
69		err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
70			if err != nil {
71				return err
72			}
73			if info.IsDir() || !filter(path) {
74				return nil
75			}
76			fileList = append(fileList, path)
77			return nil
78		})
79		if err != nil {
80			return nil, err
81		}
82	} else {
83		fileList = append(fileList, path)
84	}
85	return fileList, nil
86}
87
88// ParseValue parses string into a value
89func ParseValue(valueStr string) interface{} {
90	var value interface{}
91	if v, err := strconv.Atoi(valueStr); err == nil {
92		value = v
93	} else if v, err := strconv.ParseFloat(valueStr, 64); err == nil {
94		value = v
95	} else if v, err := strconv.ParseBool(valueStr); err == nil {
96		value = v
97	} else {
98		value = strings.ReplaceAll(valueStr, "\\,", ",")
99	}
100	return value
101}
102
103// ConsolidateLog is a helper function to dedup the log message.
104func ConsolidateLog(logMessage string) string {
105	logCountMap := make(map[string]int)
106	stderrSlice := strings.Split(logMessage, "\n")
107	for _, item := range stderrSlice {
108		if item == "" {
109			continue
110		}
111		_, exist := logCountMap[item]
112		if exist {
113			logCountMap[item]++
114		} else {
115			logCountMap[item] = 1
116		}
117	}
118	var sb strings.Builder
119	for _, item := range stderrSlice {
120		if logCountMap[item] == 0 {
121			continue
122		}
123		sb.WriteString(fmt.Sprintf("%s (repeated %v times)\n", item, logCountMap[item]))
124		// reset seen log count
125		logCountMap[item] = 0
126	}
127	return sb.String()
128}
129
130// RenderTemplate is a helper method to render a template with the given values.
131func RenderTemplate(tmpl string, ts interface{}) (string, error) {
132	t, err := template.New("").Parse(tmpl)
133	if err != nil {
134		return "", err
135	}
136	buf := new(bytes.Buffer)
137	err = t.Execute(buf, ts)
138	if err != nil {
139		return "", err
140	}
141	return buf.String(), nil
142}
143