Skip to content

Commit dc21dc3

Browse files
committed
Add generate command
A command that reads a folder with ServiceLevelObjectives and output files with the corresponding Prometheus Rules Signed-off-by: ArthurSens <[email protected]>
1 parent 3730405 commit dc21dc3

File tree

3 files changed

+97
-44
lines changed

3 files changed

+97
-44
lines changed

filesystem.go

+48-44
Original file line numberDiff line numberDiff line change
@@ -170,53 +170,11 @@ func cmdFilesystem(logger log.Logger, reg *prometheus.Registry, promClient api.C
170170
level.Debug(logger).Log("msg", "reading", "file", f)
171171
reconcilesTotal.Inc()
172172

173-
bytes, err := ioutil.ReadFile(f)
173+
objective, err := objectiveAsRuleFile(f, prometheusFolder)
174174
if err != nil {
175175
reconcilesErrors.Inc()
176-
return fmt.Errorf("failed to read file %q: %w", f, err)
176+
return fmt.Errorf("failed to create rule file %q: %w", f, err)
177177
}
178-
179-
var config v1alpha1.ServiceLevelObjective
180-
if err := yaml.UnmarshalStrict(bytes, &config); err != nil {
181-
reconcilesErrors.Inc()
182-
return fmt.Errorf("failed to unmarshal objective %q: %w", f, err)
183-
}
184-
185-
objective, err := config.Internal()
186-
if err != nil {
187-
reconcilesErrors.Inc()
188-
return fmt.Errorf("failed to get objective: %w", err)
189-
}
190-
191-
increases, err := objective.IncreaseRules()
192-
if err != nil {
193-
reconcilesErrors.Inc()
194-
return fmt.Errorf("failed to get increase rules: %w", err)
195-
}
196-
burnrates, err := objective.Burnrates()
197-
if err != nil {
198-
reconcilesErrors.Inc()
199-
return fmt.Errorf("failed to get burn rate rules: %w", err)
200-
}
201-
202-
rule := monitoringv1.PrometheusRuleSpec{
203-
Groups: []monitoringv1.RuleGroup{increases, burnrates},
204-
}
205-
206-
bytes, err = yaml.Marshal(rule)
207-
if err != nil {
208-
reconcilesErrors.Inc()
209-
return fmt.Errorf("failed to marshal recording rules: %w", err)
210-
}
211-
212-
_, file := filepath.Split(f)
213-
path := filepath.Join(prometheusFolder, file)
214-
215-
if err := ioutil.WriteFile(path, bytes, 0o644); err != nil {
216-
reconcilesErrors.Inc()
217-
return fmt.Errorf("failed to write file %q: %w", path, err)
218-
}
219-
220178
objectives.Set(objective)
221179

222180
reload <- struct{}{} // Trigger a Prometheus reload
@@ -353,3 +311,49 @@ func (f FilesystemObjectiveServer) GetREDRequests(ctx context.Context, expr, gro
353311
func (f FilesystemObjectiveServer) GetREDErrors(ctx context.Context, expr, grouping string, i, i2 int32) (openapiserver.ImplResponse, error) {
354312
return openapiserver.ImplResponse{}, errEndpointNotImplemented
355313
}
314+
315+
// objectiveAsRuleFile reads a ServiceLevelObjective YAML manifest and outputs the corresponding
316+
// Prometheus rules as a file in the desired directory.
317+
func objectiveAsRuleFile(file, prometheusFolder string) (slo.Objective, error) {
318+
bytes, err := ioutil.ReadFile(file)
319+
if err != nil {
320+
return slo.Objective{}, fmt.Errorf("failed to read file %q: %w", file, err)
321+
}
322+
323+
var config v1alpha1.ServiceLevelObjective
324+
if err := yaml.UnmarshalStrict(bytes, &config); err != nil {
325+
return slo.Objective{}, fmt.Errorf("failed to unmarshal objective %q: %w", file, err)
326+
}
327+
328+
if err != nil {
329+
return slo.Objective{}, fmt.Errorf("failed to get objective: %w", err)
330+
}
331+
332+
objective, err := config.Internal()
333+
increases, err := objective.IncreaseRules()
334+
if err != nil {
335+
return slo.Objective{}, fmt.Errorf("failed to get increase rules: %w", err)
336+
}
337+
burnrates, err := objective.Burnrates()
338+
if err != nil {
339+
return slo.Objective{}, fmt.Errorf("failed to get burn rate rules: %w", err)
340+
}
341+
342+
rule := monitoringv1.PrometheusRuleSpec{
343+
Groups: []monitoringv1.RuleGroup{increases, burnrates},
344+
}
345+
346+
bytes, err = yaml.Marshal(rule)
347+
if err != nil {
348+
return slo.Objective{}, fmt.Errorf("failed to marshal recording rules: %w", err)
349+
}
350+
351+
_, f := filepath.Split(file)
352+
path := filepath.Join(prometheusFolder, f)
353+
354+
if err := ioutil.WriteFile(path, bytes, 0o644); err != nil {
355+
return slo.Objective{}, fmt.Errorf("failed to write file %q: %w", path, err)
356+
}
357+
358+
return objective, nil
359+
}

generate.go

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
Copyright 2021 Pyrra Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"io/ioutil"
21+
"path/filepath"
22+
23+
"github.com/go-kit/log"
24+
"github.com/go-kit/log/level"
25+
)
26+
27+
func cmdGenerate(logger log.Logger, configFiles, prometheusFolder string) int {
28+
fs, err := ioutil.ReadDir(configFiles)
29+
if err != nil {
30+
level.Error(logger).Log("msg", "failed to read config-files directory", "err", err)
31+
return 1
32+
}
33+
for _, file := range fs {
34+
if !file.IsDir() {
35+
_, err := objectiveAsRuleFile(filepath.Join(configFiles, file.Name()), prometheusFolder)
36+
if err != nil {
37+
level.Error(logger).Log("msg", "failed generating rule files", "err", err)
38+
return 1
39+
}
40+
}
41+
}
42+
return 0
43+
}

main.go

+6
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ var CLI struct {
6464
MetricsAddr string `default:":8080" help:"The address the metric endpoint binds to."`
6565
ConfigMapMode bool `default:"false" help:"If the generated recording rules should instead be saved to config maps in the default Prometheus format."`
6666
} `cmd:"" help:"Runs Pyrra's Kubernetes operator and backend for the API."`
67+
Generate struct {
68+
ConfigFiles string `default:"/etc/pyrra/*.yaml" help:"The folder where Pyrra finds the config files to use."`
69+
PrometheusFolder string `default:"/etc/prometheus/pyrra/" help:"The folder where Pyrra writes the generates Prometheus rules and alerts."`
70+
} `cmd:"" help:"Read SLO config files and rewrites them as Prometheus rules and alerts."`
6771
}
6872

6973
func main() {
@@ -126,6 +130,8 @@ func main() {
126130
code = cmdFilesystem(logger, reg, client, CLI.Filesystem.ConfigFiles, CLI.Filesystem.PrometheusFolder)
127131
case "kubernetes":
128132
code = cmdKubernetes(logger, CLI.Kubernetes.MetricsAddr, CLI.Kubernetes.ConfigMapMode)
133+
case "generate":
134+
code = cmdGenerate(logger, CLI.Generate.ConfigFiles, CLI.Generate.PrometheusFolder)
129135
}
130136
os.Exit(code)
131137
}

0 commit comments

Comments
 (0)