1 package cli
2
3 import (
4 "context"
5 "errors"
6 "flag"
7 "fmt"
8 "io"
9 "os"
10 "path/filepath"
11 "regexp"
12 "strings"
13 "text/tabwriter"
14
15 "log"
16
17 "edge-infra.dev/pkg/lib/logging"
18
19 "github.com/peterbourgon/ff/v3/ffcli"
20
21 "edge-infra.dev/pkg/lib/build/bazel"
22 "edge-infra.dev/pkg/lib/cli/commands"
23 )
24
25 var (
26 Stderr io.Writer = os.Stderr
27 Println = log.Println
28 Fatalf = log.Fatalf
29 Errorf = fmt.Errorf
30 Sprintf = fmt.Sprintf
31 )
32
33 var logger = logging.NewLogger().WithName("alertman-log")
34
35 func isBoolFlag(f *flag.Flag) bool {
36 bf, ok := f.Value.(interface {
37 IsBoolFlag() bool
38 })
39 return ok && bf.IsBoolFlag()
40 }
41
42 func countFlags(fs *flag.FlagSet) (n int) {
43 fs.VisitAll(func(*flag.Flag) { n++ })
44 return n
45 }
46
47 func newFlagSet(name string) *flag.FlagSet {
48 onError := flag.ExitOnError
49 fs := flag.NewFlagSet(name, onError)
50 fs.SetOutput(Stderr)
51 return fs
52 }
53
54 var (
55 projectID string
56 wd string
57 tPath string
58 )
59
60 var globalFlags = func() *flag.FlagSet {
61 fset := flag.NewFlagSet("global", flag.ExitOnError)
62 fset.StringVar(&projectID, "project", "", "GCP Project ID where alert policy is located.")
63 return fset
64 }()
65
66
67 func withGlobalFlags(fset *flag.FlagSet) *flag.FlagSet {
68 globalFlags.VisitAll(func(f *flag.Flag) {
69 fset.Var(f.Value, f.Name, f.Usage)
70 })
71 return fset
72 }
73
74
75 func Run(args []string) (err error) {
76 rootfs := newFlagSet("alertman")
77 rootCmd := &ffcli.Command{
78 Name: "alertman",
79 ShortUsage: "alertman <subcommand> [command flags]",
80 ShortHelp: "GCP alert manager utility",
81 LongHelp: strings.TrimSpace(`
82 For help on subcommands, add --help after: "alertman get --help".
83 `),
84 Subcommands: []*ffcli.Command{
85 getCmd,
86 deleteCmd,
87 createCmd,
88 updateCmd,
89 syncCmd,
90 commands.Version(),
91 },
92 FlagSet: rootfs,
93 Exec: func(context.Context, []string) error { return flag.ErrHelp },
94 UsageFunc: usageFunc,
95 }
96
97 wd, err = bazel.ResolveWd()
98 if err != nil {
99 return errors.New(Sprintf("failed to determine current working directory: %s", err.Error()))
100 }
101
102 err = os.Chdir(wd)
103 if err != nil {
104 return err
105 }
106
107 for _, c := range rootCmd.Subcommands {
108 c.UsageFunc = usageFunc
109 }
110
111 if err = rootCmd.Parse(args); err != nil {
112 if errors.Is(err, flag.ErrHelp) {
113 return nil
114 }
115 return err
116 }
117
118 err = rootCmd.Run(context.Background())
119 if errors.Is(err, flag.ErrHelp) {
120 return nil
121 }
122 return err
123 }
124
125 func usageFunc(c *ffcli.Command) string {
126 var b strings.Builder
127
128 fmt.Fprintf(&b, "USAGE\n")
129 if c.ShortUsage != "" {
130 fmt.Fprintf(&b, " %s\n", c.ShortUsage)
131 } else {
132 fmt.Fprintf(&b, " %s\n", c.Name)
133 }
134 fmt.Fprintf(&b, "\n")
135
136 if c.LongHelp != "" {
137 fmt.Fprintf(&b, "%s\n\n", c.LongHelp)
138 }
139
140 switch {
141 case len(c.Subcommands) > 0:
142 fmt.Fprintf(&b, "SUBCOMMANDS\n")
143 tw := tabwriter.NewWriter(&b, 0, 2, 2, ' ', 0)
144 for _, subcommand := range c.Subcommands {
145 fmt.Fprintf(tw, " %s\t%s\n", subcommand.Name, subcommand.ShortHelp)
146 }
147 tw.Flush()
148 fmt.Fprintf(&b, "\n")
149 case countFlags(c.FlagSet) > 0:
150 fmt.Fprintf(&b, "FLAGS\n")
151 tw := tabwriter.NewWriter(&b, 0, 2, 2, ' ', 0)
152 c.FlagSet.VisitAll(func(f *flag.Flag) {
153 var s string
154 name, usage := flag.UnquoteUsage(f)
155 if isBoolFlag(f) {
156 s = fmt.Sprintf(" --%s, --%s=false", f.Name, f.Name)
157 } else {
158 s = fmt.Sprintf(" --%s", f.Name)
159 if len(name) > 0 {
160 s += " " + name
161 }
162 }
163
164
165 s += "\n \t"
166 s += strings.ReplaceAll(usage, "\n", "\n \t")
167
168 if f.DefValue != "" {
169 s += fmt.Sprintf(" (default %s)", f.DefValue)
170 }
171
172 fmt.Fprintln(&b, s+"\n")
173 })
174 tw.Flush()
175 fmt.Fprintf(&b, "\n")
176 }
177 return strings.TrimSpace(b.String())
178 }
179
180
181 func checkPath(path string, folder bool) bool {
182 var err error
183
184 tPath, err = filepath.Abs(path)
185 if err != nil {
186 fmt.Printf("Error evaluating absolute path: %s\n", err.Error())
187 return false
188 }
189
190 fileInfo, err := os.Stat(tPath)
191 if os.IsNotExist(err) {
192 fmt.Println("Path specified is invalid")
193 return false
194 }
195
196 if !fileInfo.IsDir() && folder {
197 fmt.Println("Path specified appears to be a file but folder path is required")
198 return false
199 }
200 return true
201 }
202
203
204 func strArray(str string, flag bool) ([]string, error) {
205 content := str
206
207 pattern := regexp.MustCompile(`(?:((?P<key>[A-Za-z0-9_-]+)(|\s+):(|\s+)(?P<value>[A-Za-z0-9_-]+))|((?P<key>[A-Za-z0-9_-]+)(|\s+):(|\s+)))`)
208
209
210 template := "$key\n$value\n"
211 templateDisplay := "$key:$value, "
212 result := []byte{}
213 resultDisplay := []byte{}
214
215
216 for _, submatches := range pattern.FindAllStringSubmatchIndex(content, -1) {
217
218 result = pattern.ExpandString(result, template, content, submatches)
219 resultDisplay = pattern.ExpandString(resultDisplay, templateDisplay, content, submatches)
220 }
221
222 if flag {
223 str1 := strings.TrimSpace(string(result))
224 zp := regexp.MustCompile(`\n`)
225 array := zp.Split(str1, -1)
226 for i := 0; i < len(array); i++ {
227 array[i] = strings.TrimSpace(array[i])
228 }
229
230 res := len(array) % 2
231 if res != 0 {
232 err := fmt.Errorf("the UserLabels {%s} is missing a Key or Value", string(resultDisplay))
233 return nil, err
234 }
235 return array, nil
236 }
237 str1 := string(result)
238 zp := regexp.MustCompile(`\n`)
239 array := zp.Split(str1, -1)
240 for i := 0; i < len(array); i++ {
241 array[i] = strings.TrimSpace(array[i])
242 }
243 return array, nil
244 }
245
View as plain text