Skip to content

Commit

Permalink
refactor getEntity
Browse files Browse the repository at this point in the history
  • Loading branch information
gsanchezgavier committed Aug 28, 2020
1 parent 284a63d commit f3d0532
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 33 deletions.
89 changes: 61 additions & 28 deletions internal/integration/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ const fileNameMatcher = `^prometheus_.*\.ya?ml$`

// Specs contains all the services specs mapped with the service name
type Specs struct {
SpecsByName map[string]Spec
SpecsByName map[string]SpecDef
}

// Spec contains the rules to group metrics into entities
type Spec struct {
// SpecDef contains the rules to group metrics into entities
type SpecDef struct {
Provider string `yaml:"provider"`
Service string `yaml:"service"`
Entities []EntityDef `yaml:"entities"`
Expand All @@ -33,16 +33,22 @@ type Spec struct {
type EntityDef struct {
Type string `yaml:"name"`
Properties PropertiesDef `yaml:"properties"`
Metrics []MetricDef `yaml:"metrics"`
}

// PropertiesDef defines the dimension used to get entity names
type PropertiesDef struct {
Dimensions []string `yaml:"dimensions"`
}

// MetricDef contains metrics definitions
type MetricDef struct {
Name string `yaml:"provider_name"`
}

// LoadSpecFiles loads all service spec files named like "prometheus_*.yml" that are in the filesPath
func LoadSpecFiles(filesPath string) (Specs, error) {
specs := Specs{SpecsByName: make(map[string]Spec)}
specs := Specs{SpecsByName: make(map[string]SpecDef)}
var files []string

filesInPath, err := ioutil.ReadDir(filesPath)
Expand All @@ -62,7 +68,7 @@ func LoadSpecFiles(filesPath string) (Specs, error) {
continue
}

var sd Spec
var sd SpecDef
err = yaml.Unmarshal(f, &sd)
if err != nil {
logrus.Errorf("fail parse service spec file %s: %s", file, err)
Expand All @@ -76,39 +82,66 @@ func LoadSpecFiles(filesPath string) (Specs, error) {
}

// getEntity returns entity name and type of the metric based on the spec configuration defined for the service.
// metric example: serviceName_entityName_metricName{dimension="dim"} 0
// serviceName, entityName and all dimensions defined in the spec should match to get an entity
// conditions for the metric:
// - metric.name has to start a prefix that matches with a service from the spec files
// - metric.name has to be defined in one of the entities of the spec file
// - if dimension has been specified for the entity, the metric need to have all of them.
// - metrics that belongs to entities with no dimension specified will share the same name
func (s *Specs) getEntity(m Metric) (entityName string, entityType string, err error) {
res := strings.Split(m.name, "_")
// We assume that minimun metric name is composed by "serviceName_entityType"
spec, err := s.findSpec(m.name)
if err != nil {
return "", "", err
}

e, ok := spec.findEntity(m.name)
if !ok {
return "", "", fmt.Errorf("metric: %s is not defined in service:%s", m.name, spec.Service)
}

entityType = spec.Provider + spec.Service + e.Type

entityName = e.Type

for _, d := range e.Properties.Dimensions {
var val interface{}
var ok bool
// the metric needs all the dimensions defined to avoid entity name collision
if val, ok = m.attributes[d]; !ok {
return "", "", fmt.Errorf("dimension %s not found in metric %s", d, m.name)
}
// entity name will be composed by the value of the dimensions defined for the entity in order
entityName = entityName + ":" + fmt.Sprintf("%v", val)
}

return entityName, entityType, nil
}

// findSpec parses the metric name to extract the service and resturns the spec definition that matches
func (s *Specs) findSpec(metricName string) (SpecDef, error) {
var spec SpecDef

res := strings.SplitN(metricName, "_", 2)
if len(res) < 2 {
return "", "", fmt.Errorf("metric: %s has no suffix to identify the entity", m.name)
return spec, fmt.Errorf("metric: %s has no suffix to identify the entity", metricName)
}
serviceName := res[0]
metricType := res[1]

var spec Spec
var ok bool
if spec, ok = s.SpecsByName[serviceName]; !ok {
return "", "", fmt.Errorf("no spec files for service: %s", serviceName)
return spec, fmt.Errorf("no spec files for service: %s", serviceName)
}
for _, e := range spec.Entities {
if metricType == e.Type {
entityType = metricType
for _, d := range e.Properties.Dimensions {
var val interface{}
var ok bool
// the metric needs all the dimensions defined to avoid entity name collision
if val, ok = m.attributes[d]; !ok {
return "", "", fmt.Errorf("dimension %s not found in metric %s", d, m.name)
}
// entity name will be composed by the value of the dimensions defined for the entity in order
entityName = entityName + ":" + fmt.Sprintf("%v", val)

return spec, nil
}

// findEntity returns the entity where metricName is defined
func (s *SpecDef) findEntity(metricName string) (EntityDef, bool) {
for _, e := range s.Entities {
for _, em := range e.Metrics {
if metricName == em.Name {
return e, true
}
break
}
}
entityName = strings.TrimPrefix(entityName, ":")
return entityName, entityType, nil
return EntityDef{}, false
}
28 changes: 23 additions & 5 deletions internal/integration/spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestSpecs_getEntity(t *testing.T) {
assert.Contains(t, specs.SpecsByName, "ravendb")

type fields struct {
SpecsByName map[string]Spec
SpecsByName map[string]SpecDef
}
type args struct {
m Metric
Expand All @@ -51,8 +51,20 @@ func TestSpecs_getEntity(t *testing.T) {
"database": "test",
},
}},
wantEntityName: "test",
wantEntityType: "database",
wantEntityName: "database:test",
wantEntityType: "prometheusravendbdatabase",
wantErr: false,
},
{
name: "matchEntityWithoutDimensions",
fields: fields{specs.SpecsByName},
args: args{
Metric{
name: "ravendb_document_put_bytes_total",
attributes: labels.Set{},
}},
wantEntityName: "node",
wantEntityType: "prometheusravendbnode",
wantErr: false,
},
{
Expand All @@ -66,8 +78,8 @@ func TestSpecs_getEntity(t *testing.T) {
"dim2": "second",
},
}},
wantEntityName: "first:second",
wantEntityType: "testentity",
wantEntityName: "testentity:first:second",
wantEntityType: "prometheusravendbtestentity",
wantErr: false,
},
{
Expand All @@ -82,6 +94,12 @@ func TestSpecs_getEntity(t *testing.T) {
args: args{Metric{name: "service_metric_undefined"}},
wantErr: true,
},
{
name: "metricNotDefined",
fields: fields{specs.SpecsByName},
args: args{Metric{name: "ravendb_metric_undefined"}},
wantErr: true,
},
{
name: "shortMetricName",
fields: fields{specs.SpecsByName},
Expand Down
3 changes: 3 additions & 0 deletions internal/integration/test/prometheus_ibmmq.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ entities:
- provider_name: ibmmq_qmgr_commit_count
description: Commit count
unit: Count
- provider_name: go_goroutines
description: Number of goroutines that currently exist.
unit: gauge
9 changes: 9 additions & 0 deletions internal/integration/test/prometheus_ravendb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ provider: prometheus
service: ravendb
display_name: Raven Db
entities:
- name: node
display_name: RavenDb Node
metrics:
- provider_name: ravendb_document_put_bytes_total
description: Server-wide document put bytes
unit: Count
- provider_name: ravendb_is_leader
description: If 1, then node is the cluster leader, otherwise 0
unit: Gauge
- name: database
display_name: Database
properties:
Expand Down

0 comments on commit f3d0532

Please sign in to comment.