1 package cli
2
3 import (
4 "context"
5 "flag"
6 "fmt"
7 "strings"
8
9 "github.com/peterbourgon/ff/v3/ffcli"
10
11 "edge-infra.dev/pkg/lib/gcp/monitoring/metrics"
12 "edge-infra.dev/pkg/lib/gcp/monitoring/monutil"
13 )
14
15 type alignArgsT struct {
16 noPrompt bool
17 }
18
19 var alignArgs alignArgsT
20 var alignFlagSet = newAlignFlagSet(&alignArgs)
21
22
23 func newAlignFlagSet(alignArgs *alignArgsT) *flag.FlagSet {
24 alignf := newFlagSet("align")
25 alignf.BoolVar(&alignArgs.noPrompt, "no-prompt", false, "Determines whether the user will be prompted to confirm each descriptor alignment action.")
26 return alignf
27 }
28
29 var alignCmd = &ffcli.Command{
30 Name: "align",
31 ShortUsage: "align [flags]",
32 ShortHelp: "align metric descriptor labels",
33 LongHelp: strings.TrimSpace(`
34 Aligns metricDescriptor labels from two or more projects.
35 `),
36 FlagSet: withGlobalFlags(alignFlagSet),
37 Exec: runAlign,
38 }
39
40 func runAlign(ctx context.Context, args []string) error {
41 var err error
42
43 if len(args) > 0 {
44 Fatalf("too many non-flag arguments: %q", args)
45 }
46 if !checkAlignFlags() || !checkGlobalFlags() {
47 return flag.ErrHelp
48 }
49
50
51 projects := strings.Split(strings.ReplaceAll(projectID, " ", ""), ",")
52 d, err := retrieveDescriptors(ctx, projects)
53 if err != nil {
54 return Errorf("failed to metricDescriptors for multiple projects: %w", err)
55 }
56
57
58 if filterForeman {
59 projects := strings.Split(strings.ReplaceAll(projectID, " ", ""), ",")
60 d, err = d.FilterProjects(projects)
61 if err != nil {
62 return err
63 }
64 }
65
66
67 cd := d.Compare()
68
69
70 var ads alignDescriptors
71 for i := 0; i < len(cd.Descriptor); i++ {
72 if len(cd.Descriptor[i].DiffLabels) > 0 {
73 ads = append(ads, alignDescriptor{
74 Name: cd.Descriptor[i].Name,
75 CommonLabels: cd.Descriptor[i].CommonLabels,
76 DiffLabels: cd.Descriptor[i].DiffLabels,
77 })
78 }
79 }
80
81 ar, err := ads.align(ctx)
82 if err != nil {
83 return err
84 }
85
86 if jsonFormat {
87 _ = metrics.TmplJSON.Execute(Stdout, ar)
88 } else if !silent {
89 monutil.ClearTerminal()
90 _ = metrics.TmplAlignReport.Execute(Stdout, ar)
91 fmt.Printf("\n\n%s", monutil.White(mmBroomTitle))
92 }
93 return nil
94 }
95
96
97 func checkAlignFlags() bool {
98 return true
99 }
100
101 type alignDescriptors []alignDescriptor
102 type alignDescriptor metrics.AlignDescriptor
103
104
105 func (ads alignDescriptors) align(ctx context.Context) (metrics.AlignReport, error) {
106 var ar metrics.AlignReport
107 for i := 0; i < len(ads); i++ {
108 ar.Misaligned++
109 a, err := ads[i].align(ctx)
110 if err != nil && a.Name == "" {
111 ar.Attempted++
112 ar.Failed = append(ar.Failed, ads[i].Name)
113 } else if err != nil && a.Name != "" {
114 ar.Attempted++
115 ar.Skipped = append(ar.Skipped, a.Name)
116 } else {
117 ar.Completed++
118 ar.Aligned = append(ar.Aligned, a.Name)
119 }
120 }
121
122 if (ar.Attempted + ar.Completed) != ar.Misaligned {
123 return ar, fmt.Errorf("the align result count of Attempted (%d) + Completed (%d) does not equal the total Misaligned (%d) count", ar.Attempted, ar.Completed, ar.Misaligned)
124 }
125 return ar, nil
126 }
127
128
129 func (ad alignDescriptor) align(ctx context.Context) (*metrics.Descriptor, error) {
130 project := metrics.ParseProjectID(ad.Name)
131 metric := metrics.ParseMetricName(ad.Name)
132 prefix := metrics.ParsePrefix(ad.Name)
133 allLabels := append(ad.CommonLabels, ad.DiffLabels...)
134
135
136 client, err := metrics.New(ctx, project)
137 if err != nil {
138 return nil, err
139 }
140
141
142 d, err := client.GetDescriptor(prefix, metric)
143 if err != nil {
144 return nil, err
145 }
146
147 var aConfirm = false
148 if !silent && !alignArgs.noPrompt {
149
150 monutil.ClearTerminal()
151 q := *new(strings.Builder)
152 _ = metrics.TmplAlignConfirm.Execute(&q, ad)
153 aConfirm = monutil.PromptYesNoCancel(q.String())
154 fmt.Println()
155 }
156
157
158 if silent || alignArgs.noPrompt || aConfirm {
159 if client.AddTimeSeriesLabels(d, allLabels) {
160 r, err := client.GetDescriptor(prefix, metric)
161 if err != nil {
162 return nil, err
163 }
164 return r, nil
165 }
166 }
167
168
169 return &metrics.Descriptor{
170 Name: ad.Name,
171 CommonLabels: ad.CommonLabels,
172 DiffLabels: ad.DiffLabels,
173 }, fmt.Errorf("skipping metric %s descriptor alignment", ad.Name)
174 }
175
View as plain text