blob: e94e90f206c4361421d94733929fc42502877f85 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package config_validator
import (
"config"
"fmt"
"github.com/golang/glog"
"regexp"
"time"
)
// Note that when reports are serialized to CSV, the column headers used are
// derived from metric part names. Consequently, if this regular expression
// is modified then the code in the function
// EscapeMetricPartNameForCSVColumHeader() in
// //analyzer/report_master/report_serializer.cc must be modified.
var validMetricPartName = regexp.MustCompile("^[a-zA-Z][_a-zA-Z0-9\\- ]+$")
func validateConfiguredMetrics(config *config.CobaltConfig) (err error) {
// Set of encodingIds. Used to check that the Metric only refers to valid encodingIds.
encodingIds := map[string]bool{}
// Set of metric ids. Used to detect duplicates.
metricIds := map[string]bool{}
for _, encoding := range config.EncodingConfigs {
encodingIds[formatId(encoding.CustomerId, encoding.ProjectId, encoding.Id)] = true
}
for i, metric := range config.MetricConfigs {
metricKey := formatId(metric.CustomerId, metric.ProjectId, metric.Id)
if metricIds[metricKey] {
return fmt.Errorf("Metric id %s is repeated in metric config entry number %v. Metric ids must be unique.", metricKey, i+1)
}
metricIds[metricKey] = true
if err = validateMetric(metric, encodingIds); err != nil {
return fmt.Errorf("Error validating metric %v (%v, %v, %v): %v", metric.Name, metric.CustomerId, metric.ProjectId, metric.Id, err)
}
}
return nil
}
func validateMetric(m *config.Metric, encodingIds map[string]bool) (err error) {
if m.Id == 0 {
return fmt.Errorf("Metric id '0' is invalid.")
}
if m.ProjectId >= 100 {
if m.GetMetaData() == nil || m.GetMetaData().ExpiresAfter == "" {
return fmt.Errorf("expires_after is not present. All metrics with project_id > 100 must have an expires_after field set.")
}
oldestValidExpiry := time.Now().AddDate(1, 0, 0)
date, err := time.Parse("2006/01/02", m.GetMetaData().ExpiresAfter)
if err != nil {
return fmt.Errorf("Unable to parse expires_after. Format should be yyyy/mm/dd. %v", err)
}
if date.After(oldestValidExpiry) {
return fmt.Errorf("Expiry date '%v' is past the maximum expiry date of '%v'", date, oldestValidExpiry)
}
// We don't currently enforce expiry dates in code, but we should warn about it.
if date.Before(time.Now()) {
glog.Warningf("Metric '%v' (Customer %v, Project %v, Id %v) has expired.", m.Name, m.CustomerId, m.ProjectId, m.Id)
} else if date.Before(time.Now().AddDate(0, 3, 0)) {
glog.Warningf("Metric '%v' (Customer %v, Project %v, Id %v) will expire within 3 months.", m.Name, m.CustomerId, m.ProjectId, m.Id)
}
}
for name, v := range m.Parts {
if v == nil {
return fmt.Errorf("Metric part '%v' is null. This is not allowed.", name)
}
if !validMetricPartName.MatchString(name) {
return fmt.Errorf("Metric part name '%v' is invalid. Metric part names must match the regular expression '%v'.", name, validMetricPartName)
}
encodingId := formatId(m.CustomerId, m.ProjectId, v.DefaultEncodingId)
if !encodingIds[encodingId] {
return fmt.Errorf("Metric part '%v' is invalid. There is no encoding id %v.", name, encodingId)
}
}
return nil
}