Skip to content

Commit 335b597

Browse files
authored
Merge pull request #361 from ArthurSens/arthursens/standalone-cli-tool-314
Add generate command
2 parents 2584cef + 1d85de1 commit 335b597

File tree

3 files changed

+116
-55
lines changed

3 files changed

+116
-55
lines changed

filesystem.go

Lines changed: 66 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -171,68 +171,17 @@ func cmdFilesystem(logger log.Logger, reg *prometheus.Registry, promClient api.C
171171
level.Debug(logger).Log("msg", "reading", "file", f)
172172
reconcilesTotal.Inc()
173173

174-
bytes, err := os.ReadFile(f)
174+
err := writeRuleFile(logger, f, prometheusFolder, genericRules)
175175
if err != nil {
176176
reconcilesErrors.Inc()
177-
return fmt.Errorf("failed to read file %q: %w", f, err)
177+
level.Error(logger).Log("msg", "error creating rule file", "file", f)
178178
}
179179

180-
var config v1alpha1.ServiceLevelObjective
181-
if err := yaml.UnmarshalStrict(bytes, &config); err != nil {
182-
reconcilesErrors.Inc()
183-
return fmt.Errorf("failed to unmarshal objective %q: %w", f, err)
184-
}
185-
186-
objective, err := config.Internal()
187-
if err != nil {
188-
reconcilesErrors.Inc()
189-
return fmt.Errorf("failed to get objective: %w", err)
190-
}
191-
192-
increases, err := objective.IncreaseRules()
193-
if err != nil {
194-
reconcilesErrors.Inc()
195-
return fmt.Errorf("failed to get increase rules: %w", err)
196-
}
197-
burnrates, err := objective.Burnrates()
198-
if err != nil {
199-
reconcilesErrors.Inc()
200-
return fmt.Errorf("failed to get burn rate rules: %w", err)
201-
}
202-
203-
rule := monitoringv1.PrometheusRuleSpec{
204-
Groups: []monitoringv1.RuleGroup{increases, burnrates},
205-
}
206-
207-
if genericRules {
208-
rules, err := objective.GenericRules()
209-
if err == nil {
210-
rule.Groups = append(rule.Groups, rules)
211-
} else {
212-
if err != slo.ErrGroupingUnsupported {
213-
return fmt.Errorf("failed to get generic rules: %w", err)
214-
}
215-
level.Warn(logger).Log(
216-
"msg", "objective with grouping unsupported with generic rules",
217-
"objective", objective.Name(),
218-
)
219-
}
220-
}
221-
222-
bytes, err = yaml.Marshal(rule)
180+
objective, err := objectiveFromFile(f)
223181
if err != nil {
224182
reconcilesErrors.Inc()
225-
return fmt.Errorf("failed to marshal recording rules: %w", err)
226-
}
227-
228-
_, file := filepath.Split(f)
229-
path := filepath.Join(prometheusFolder, file)
230-
231-
if err := os.WriteFile(path, bytes, 0o644); err != nil {
232-
reconcilesErrors.Inc()
233-
return fmt.Errorf("failed to write file %q: %w", path, err)
183+
level.Error(logger).Log("msg", "failed to get objective from file", "file", f)
234184
}
235-
236185
objectives.Set(objective)
237186

238187
reload <- struct{}{} // Trigger a Prometheus reload
@@ -343,3 +292,65 @@ func (s *FilesystemObjectiveServer) List(ctx context.Context, req *connect.Reque
343292
Objectives: objectives,
344293
}), nil
345294
}
295+
296+
func writeRuleFile(logger log.Logger, file, prometheusFolder string, genericRules bool) error {
297+
objective, err := objectiveFromFile(file)
298+
if err != nil {
299+
return fmt.Errorf("failed to get objective: %w", err)
300+
}
301+
302+
increases, err := objective.IncreaseRules()
303+
if err != nil {
304+
return fmt.Errorf("failed to get increase rules: %w", err)
305+
}
306+
burnrates, err := objective.Burnrates()
307+
if err != nil {
308+
return fmt.Errorf("failed to get burn rate rules: %w", err)
309+
}
310+
311+
rule := monitoringv1.PrometheusRuleSpec{
312+
Groups: []monitoringv1.RuleGroup{increases, burnrates},
313+
}
314+
315+
if genericRules {
316+
rules, err := objective.GenericRules()
317+
if err == nil {
318+
rule.Groups = append(rule.Groups, rules)
319+
} else {
320+
if err != slo.ErrGroupingUnsupported {
321+
return fmt.Errorf("failed to get generic rules: %w", err)
322+
}
323+
level.Warn(logger).Log(
324+
"msg", "objective with grouping unsupported with generic rules",
325+
"objective", objective.Name(),
326+
)
327+
}
328+
}
329+
330+
bytes, err := yaml.Marshal(rule)
331+
if err != nil {
332+
return fmt.Errorf("failed to marshal recording rules: %w", err)
333+
}
334+
335+
_, f := filepath.Split(file)
336+
path := filepath.Join(prometheusFolder, f)
337+
338+
if err := os.WriteFile(path, bytes, 0o644); err != nil {
339+
return fmt.Errorf("failed to write file %q: %w", path, err)
340+
}
341+
return nil
342+
}
343+
344+
func objectiveFromFile(file string) (slo.Objective, error) {
345+
bytes, err := os.ReadFile(file)
346+
if err != nil {
347+
return slo.Objective{}, fmt.Errorf("failed to read file %q: %w", file, err)
348+
}
349+
350+
var config v1alpha1.ServiceLevelObjective
351+
if err := yaml.UnmarshalStrict(bytes, &config); err != nil {
352+
return slo.Objective{}, fmt.Errorf("failed to unmarshal objective %q: %w", file, err)
353+
}
354+
355+
return config.Internal()
356+
}

generate.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
Copyright 2023 Pyrra Authors.
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+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package main
15+
16+
import (
17+
"path/filepath"
18+
19+
"github.com/go-kit/log"
20+
"github.com/go-kit/log/level"
21+
)
22+
23+
func cmdGenerate(logger log.Logger, configFiles, prometheusFolder string, genericRules bool) int {
24+
filenames, err := filepath.Glob(configFiles)
25+
if err != nil {
26+
level.Error(logger).Log("msg", "getting file names", "err", err)
27+
return 1
28+
}
29+
30+
for _, file := range filenames {
31+
err := writeRuleFile(logger, file, prometheusFolder, genericRules)
32+
if err != nil {
33+
level.Error(logger).Log("msg", "generating rule files", "err", err)
34+
return 1
35+
}
36+
}
37+
return 0
38+
}

main.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ var CLI struct {
6969
ConfigMapMode bool `default:"false" help:"If the generated recording rules should instead be saved to config maps in the default Prometheus format."`
7070
GenericRules bool `default:"false" help:"Enabled generic recording rules generation to make it easier for tools like Grafana."`
7171
} `cmd:"" help:"Runs Pyrra's Kubernetes operator and backend for the API."`
72+
Generate struct {
73+
ConfigFiles string `default:"/etc/pyrra/*.yaml" help:"The folder where Pyrra finds the config files to use."`
74+
PrometheusFolder string `default:"/etc/prometheus/pyrra/" help:"The folder where Pyrra writes the generated Prometheus rules and alerts."`
75+
GenericRules bool `default:"false" help:"Enabled generic recording rules generation to make it easier for tools like Grafana."`
76+
} `cmd:"" help:"Read SLO config files and rewrites them as Prometheus rules and alerts."`
7277
}
7378

7479
func main() {
@@ -151,6 +156,13 @@ func main() {
151156
CLI.Kubernetes.ConfigMapMode,
152157
CLI.Kubernetes.GenericRules,
153158
)
159+
case "generate":
160+
code = cmdGenerate(
161+
logger,
162+
CLI.Generate.ConfigFiles,
163+
CLI.Generate.PrometheusFolder,
164+
CLI.Generate.GenericRules,
165+
)
154166
}
155167
os.Exit(code)
156168
}

0 commit comments

Comments
 (0)