blob: 7a2597e8eea08a3d3db33fbd878c45cac3d86c6e [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"
)
func validateConfiguredReports(config *config.CobaltConfig) (err error) {
// Mapping of metric ids to their order in the MetricConfigs slice.
metrics := map[string]uint32{}
// Set of report ids. Used to detect duplicates.
reportIds := map[string]bool{}
for i, metric := range config.MetricConfigs {
metrics[formatId(metric.CustomerId, metric.ProjectId, metric.Id)] = uint32(i)
}
for i, report := range config.ReportConfigs {
if report.Id == 0 {
return fmt.Errorf("Error validating report %v: Report id '0' is invalid.", report.Name)
}
reportKey := formatId(report.CustomerId, report.ProjectId, report.Id)
if reportIds[reportKey] {
return fmt.Errorf("Report id %s is repeated in report config entry number %v. Report ids must be unique.", reportKey, i+1)
}
reportIds[reportKey] = true
metricKey := formatId(report.CustomerId, report.ProjectId, report.MetricId)
if _, ok := metrics[metricKey]; !ok {
return fmt.Errorf("Error validating report %v (%v): There is no metric id %v.", report.Name, report.Id, metricKey)
}
metric := config.MetricConfigs[metrics[metricKey]]
if err := validateReportVariables(report, metric); err != nil {
return fmt.Errorf("Error validating report %v (%v): %v", report.Name, report.Id, err)
}
for exportConfigIdx, exportConfig := range report.ExportConfigs {
if exportConfig.ExportSerialization == nil {
return fmt.Errorf("Error validating report %v (%v): element %v of export_configs has no export serialization set.", report.Name, report.Id, exportConfigIdx)
}
if exportConfig.ExportLocation == nil {
return fmt.Errorf("Error validating report %v (%v): element %v of export_configs has no export location set.", report.Name, report.Id, exportConfigIdx)
}
}
}
return nil
}
// Checks that the report variables are compatible with the specific metric.
func validateReportVariables(c *config.ReportConfig, m *config.Metric) (err error) {
if len(c.Variable) == 0 {
glog.Warningf("Report '%v' (Customer %v, Project %v Id %v) does not have any report variables.", c.Name, c.CustomerId, c.ProjectId, c.Id)
return nil
}
for i, v := range c.Variable {
if v == nil {
return fmt.Errorf("Report Variable in position %v is null. This is not allowed.", i)
}
// Check that the metric part being referenced can be found.
p, ok := m.Parts[v.MetricPart]
if !ok {
return fmt.Errorf("Metric part '%v' is not present in metric '%v'.", v.MetricPart, m.Name)
}
// Checks that if index labels are found, the metric part referred to is an index.
if v.IndexLabels != nil && len(v.IndexLabels.Labels) > 0 && p.DataType != config.MetricPart_INDEX {
return fmt.Errorf("Report variable %v has index labels specified "+
"which implies referring to an index metric part. But metric part '%v'"+
"of metric '%v' (%v) is of type %v.",
i, v.MetricPart, m.Name, m.Id, config.MetricPart_DataType_name[int32(p.DataType)])
}
// Checks that if RAPPOR candidates are found, the metric part referred to is a string.
if v.RapporCandidates != nil && len(v.RapporCandidates.Candidates) > 0 && p.DataType != config.MetricPart_STRING {
return fmt.Errorf("Report variable %v has RAPPOR candidates specified "+
"which implies referring to a string metric part. But metric part '%v'"+
"of metric '%v' (%v) is of type %v.",
i, v.MetricPart, m.Name, m.Id, config.MetricPart_DataType_name[int32(p.DataType)])
}
}
return nil
}