Skip to content

Commit 2e63b75

Browse files
Merge pull request openshift#218 from jeefy/SDCICD-51
SDCICD-51: Support arbitrary metrics derived from log data
2 parents 12816c0 + df772ba commit 2e63b75

File tree

5 files changed

+85
-25
lines changed

5 files changed

+85
-25
lines changed

common/e2e.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os"
1010
"path"
1111
"path/filepath"
12+
"regexp"
1213
"strings"
1314
"testing"
1415

@@ -206,6 +207,29 @@ func runTestsInPhase(t *testing.T, phase string, description string) {
206207
}
207208
}
208209
}
210+
211+
logMetricsRegexs := make(map[string]*regexp.Regexp)
212+
for name, match := range cfg.LogMetrics {
213+
logMetricsRegexs[name] = regexp.MustCompile(match)
214+
}
215+
216+
files, err = ioutil.ReadDir(cfg.ReportDir)
217+
if err != nil {
218+
t.Fatalf("error reading phase directory: %s", err.Error())
219+
}
220+
221+
for _, file := range files {
222+
if logFileRegex.MatchString(file.Name()) {
223+
data, err := ioutil.ReadFile(filepath.Join(cfg.ReportDir, file.Name()))
224+
if err != nil {
225+
t.Fatalf("error opening log file %s: %s", file.Name(), err.Error())
226+
}
227+
for name, matchRegex := range logMetricsRegexs {
228+
matches := matchRegex.Find(data)
229+
metadata.Instance.IncrementLogMetric(name, len(matches))
230+
}
231+
}
232+
}
209233
}
210234

211235
// checkBeforeMetricsGeneration runs a variety of checks before generating metrics.

common/metrics.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const (
3232
eventMetricName string = cicdPrefix + "event"
3333
)
3434

35-
var junitFileRegex *regexp.Regexp
35+
var junitFileRegex, logFileRegex *regexp.Regexp
3636

3737
// Metrics is the metrics object which can parse jUnit and JSON metadata and produce Prometheus metrics.
3838
type Metrics struct {
@@ -87,6 +87,7 @@ func NewMetrics() *Metrics {
8787

8888
func init() {
8989
junitFileRegex = regexp.MustCompile("^junit.*\\.xml$")
90+
logFileRegex = regexp.MustCompile("^.*\\.(log|txt)$")
9091
}
9192

9293
// WritePrometheusFile collects data and writes it out in the prometheus export file format (https://github.com/prometheus/docs/blob/master/content/docs/instrumenting/exposition_formats.md)

pkg/config/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ type Config struct {
2929

3030
// DryRun lets you run osde2e all the way up to the e2e tests then skips them.
3131
DryRun bool `json:"dry_run,omitempty" env:"DRY_RUN" sect:"tests" yaml:"dryRun"`
32+
33+
// LogMetrics lets you define a metric name and a regex to apply on the build log
34+
// For every match in the build log, the metric with that name will increment
35+
LogMetrics map[string]string `json:"log-metrics" yaml:"logMetrics"`
3236
}
3337

3438
// KubeConfig stores information required to talk to the Kube API

pkg/metadata/metadata.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,17 @@ type Metadata struct {
3131
UpgradeVersionSource string `json:"upgrade-version-source,omitempty"`
3232

3333
// Metrics
34-
TimeToOCMReportingInstalled float64 `json:"time-to-ocm-reporting-installed,string"`
35-
TimeToClusterReady float64 `json:"time-to-cluster-ready,string"`
34+
TimeToOCMReportingInstalled float64 `json:"time-to-ocm-reporting-installed,string"`
35+
TimeToClusterReady float64 `json:"time-to-cluster-ready,string"`
36+
LogMetrics map[string]int `json:"log-metrics"`
3637
}
3738

3839
// Instance is the global metadata instance
3940
var Instance *Metadata
4041

4142
func init() {
4243
Instance = &Metadata{}
44+
Instance.LogMetrics = make(map[string]int)
4345
}
4446

4547
// Next are a bunch of setter functions that allow us
@@ -94,6 +96,17 @@ func (m *Metadata) SetTimeToClusterReady(timeToClusterReady float64) {
9496
m.WriteToJSON(config.Instance.ReportDir)
9597
}
9698

99+
// IncrementLogMetric adds a supplied number to a log metric or sets the metric to
100+
// the value if it doesn't exist already
101+
func (m *Metadata) IncrementLogMetric(metric string, value int) {
102+
if _, ok := m.LogMetrics[metric]; ok {
103+
m.LogMetrics[metric] += value
104+
} else {
105+
m.LogMetrics[metric] = value
106+
}
107+
m.WriteToJSON(config.Instance.ReportDir)
108+
}
109+
97110
// WriteToJSON will marshall the metadata struct and write it into the given file.
98111
func (m *Metadata) WriteToJSON(reportDir string) (err error) {
99112
var data []byte

pkg/metadata/metadata_test.go

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,31 @@ import (
1010
"testing"
1111
)
1212

13+
// generateExpected marshals/unmarshals a supplied metadata object.
14+
// Rather than hand-curating our own expected data, this functionally
15+
// achieves the same test result but it's cleaner.
16+
func generateExpected(m *Metadata) map[string]interface{} {
17+
tmp, err := json.Marshal(m)
18+
if err != nil {
19+
fmt.Printf("%s", err.Error())
20+
return nil
21+
}
22+
23+
data := make(map[string]interface{})
24+
25+
err = json.Unmarshal(tmp, &data)
26+
if err != nil {
27+
fmt.Printf("%s\n", err.Error())
28+
return nil
29+
}
30+
31+
return data
32+
}
33+
1334
func TestMetadata(t *testing.T) {
1435
tests := []struct {
15-
name string
16-
m *Metadata
17-
expected map[string]interface{}
36+
name string
37+
m *Metadata
1838
}{
1939
{
2040
name: "test all fields",
@@ -27,15 +47,6 @@ func TestMetadata(t *testing.T) {
2747
TimeToOCMReportingInstalled: 123.45,
2848
TimeToClusterReady: 456.78,
2949
},
30-
expected: map[string]interface{}{
31-
"cluster-id": "test-id",
32-
"cluster-name": "test-name",
33-
"cluster-version": "test-version",
34-
"environment": "test-environment",
35-
"upgrade-version": "test-upgrade",
36-
"time-to-ocm-reporting-installed": "123.45",
37-
"time-to-cluster-ready": "456.78",
38-
},
3950
},
4051
{
4152
name: "omit upgrade version",
@@ -47,25 +58,32 @@ func TestMetadata(t *testing.T) {
4758
TimeToOCMReportingInstalled: 123.45,
4859
TimeToClusterReady: 456.78,
4960
},
50-
expected: map[string]interface{}{
51-
"cluster-id": "test-id",
52-
"cluster-name": "test-name",
53-
"cluster-version": "test-version",
54-
"environment": "test-environment",
55-
"time-to-ocm-reporting-installed": "123.45",
56-
"time-to-cluster-ready": "456.78",
61+
},
62+
{
63+
name: "log metrics exists",
64+
m: &Metadata{
65+
ClusterID: "test-id",
66+
ClusterName: "test-name",
67+
ClusterVersion: "test-version",
68+
Environment: "test-environment",
69+
UpgradeVersion: "test-upgrade",
70+
TimeToOCMReportingInstalled: 123.45,
71+
TimeToClusterReady: 456.78,
72+
LogMetrics: map[string]int{
73+
"some-metric": 5,
74+
},
5775
},
5876
},
5977
}
6078

6179
for _, test := range tests {
62-
if err := writeAndTestMetadata(test.m, test.expected); err != nil {
80+
if err := writeAndTestMetadata(test.m); err != nil {
6381
t.Errorf("%s: error while testing metadata: %v", test.name, err)
6482
}
6583
}
6684
}
6785

68-
func writeAndTestMetadata(m *Metadata, expected map[string]interface{}) (err error) {
86+
func writeAndTestMetadata(m *Metadata) (err error) {
6987
var tempDir string
7088
if tempDir, err = ioutil.TempDir("", ""); err != nil {
7189
return err
@@ -87,7 +105,7 @@ func writeAndTestMetadata(m *Metadata, expected map[string]interface{}) (err err
87105
return err
88106
}
89107

90-
if !reflect.DeepEqual(expected, readData) {
108+
if !reflect.DeepEqual(generateExpected(m), readData) {
91109
return fmt.Errorf("expected and generated JSON do not match")
92110
}
93111

0 commit comments

Comments
 (0)