blob: 5d6fa5684b73e5a091f7ee471fc581b6b25d4b20 [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"
)
// This file contains logic to validate list of ReportDefinition protos in MetricDefinition protos.
// Maps MetricTypes to valid ReportTypes.
var allowedReportTypes = map[config.MetricDefinition_MetricType]map[config.ReportDefinition_ReportType]bool{
config.MetricDefinition_EVENT_OCCURRED: map[config.ReportDefinition_ReportType]bool{
config.ReportDefinition_SIMPLE_OCCURRENCE_COUNT: true,
config.ReportDefinition_UNIQUE_N_DAY_ACTIVES: true,
},
config.MetricDefinition_EVENT_COUNT: map[config.ReportDefinition_ReportType]bool{
config.ReportDefinition_EVENT_COMPONENT_OCCURRENCE_COUNT: true,
config.ReportDefinition_INT_RANGE_HISTOGRAM: true,
config.ReportDefinition_NUMERIC_AGGREGATION: true,
config.ReportDefinition_PER_DEVICE_NUMERIC_STATS: true,
config.ReportDefinition_PER_DEVICE_HISTOGRAM: true,
},
config.MetricDefinition_ELAPSED_TIME: map[config.ReportDefinition_ReportType]bool{
config.ReportDefinition_INT_RANGE_HISTOGRAM: true,
config.ReportDefinition_NUMERIC_AGGREGATION: true,
config.ReportDefinition_PER_DEVICE_NUMERIC_STATS: true,
config.ReportDefinition_PER_DEVICE_HISTOGRAM: true,
},
config.MetricDefinition_FRAME_RATE: map[config.ReportDefinition_ReportType]bool{
config.ReportDefinition_INT_RANGE_HISTOGRAM: true,
config.ReportDefinition_NUMERIC_AGGREGATION: true,
config.ReportDefinition_PER_DEVICE_NUMERIC_STATS: true,
config.ReportDefinition_PER_DEVICE_HISTOGRAM: true,
},
config.MetricDefinition_MEMORY_USAGE: map[config.ReportDefinition_ReportType]bool{
config.ReportDefinition_INT_RANGE_HISTOGRAM: true,
config.ReportDefinition_NUMERIC_AGGREGATION: true,
config.ReportDefinition_PER_DEVICE_NUMERIC_STATS: true,
config.ReportDefinition_PER_DEVICE_HISTOGRAM: true,
},
config.MetricDefinition_INT_HISTOGRAM: map[config.ReportDefinition_ReportType]bool{
config.ReportDefinition_INT_RANGE_HISTOGRAM: true,
},
config.MetricDefinition_OCCURRENCE: map[config.ReportDefinition_ReportType]bool{
config.ReportDefinition_FLEETWIDE_OCCURRENCE_COUNTS: true,
config.ReportDefinition_UNIQUE_DEVICE_COUNTS: true,
config.ReportDefinition_UNIQUE_DEVICE_HISTOGRAMS: true,
config.ReportDefinition_HOURLY_VALUE_HISTOGRAMS: true,
config.ReportDefinition_UNIQUE_DEVICE_NUMERIC_STATS: true,
config.ReportDefinition_HOURLY_VALUE_NUMERIC_STATS: true,
},
config.MetricDefinition_INTEGER: map[config.ReportDefinition_ReportType]bool{
config.ReportDefinition_UNIQUE_DEVICE_HISTOGRAMS: true,
config.ReportDefinition_HOURLY_VALUE_HISTOGRAMS: true,
config.ReportDefinition_FLEETWIDE_HISTOGRAMS: true,
config.ReportDefinition_FLEETWIDE_MEANS: true,
config.ReportDefinition_UNIQUE_DEVICE_NUMERIC_STATS: true,
config.ReportDefinition_HOURLY_VALUE_NUMERIC_STATS: true,
},
config.MetricDefinition_INTEGER_HISTOGRAM: map[config.ReportDefinition_ReportType]bool{
config.ReportDefinition_FLEETWIDE_HISTOGRAMS: true,
},
config.MetricDefinition_STRING: map[config.ReportDefinition_ReportType]bool{
config.ReportDefinition_STRING_COUNTS: true,
config.ReportDefinition_UNIQUE_DEVICE_STRING_COUNTS: true,
},
config.MetricDefinition_CUSTOM: map[config.ReportDefinition_ReportType]bool{
config.ReportDefinition_CUSTOM_RAW_DUMP: true,
},
}
func reportTypeDeprecatedInCobalt11(r config.ReportDefinition_ReportType) bool {
switch r {
case config.ReportDefinition_SIMPLE_OCCURRENCE_COUNT,
config.ReportDefinition_EVENT_COMPONENT_OCCURRENCE_COUNT,
config.ReportDefinition_NUMERIC_AGGREGATION,
config.ReportDefinition_INT_RANGE_HISTOGRAM,
config.ReportDefinition_UNIQUE_N_DAY_ACTIVES,
config.ReportDefinition_PER_DEVICE_NUMERIC_STATS,
config.ReportDefinition_PER_DEVICE_HISTOGRAM:
return true
default:
return false
}
}
// For reports that must specify a policy, this is the suggestion we will give.
var SuggestedSystemProfileSelectionPolicy = map[config.ReportDefinition_ReportType]config.SystemProfileSelectionPolicy{
config.ReportDefinition_HOURLY_VALUE_HISTOGRAMS: config.SystemProfileSelectionPolicy_SELECT_LAST,
config.ReportDefinition_UNIQUE_DEVICE_COUNTS: config.SystemProfileSelectionPolicy_SELECT_LAST,
config.ReportDefinition_UNIQUE_DEVICE_HISTOGRAMS: config.SystemProfileSelectionPolicy_SELECT_LAST,
config.ReportDefinition_UNIQUE_DEVICE_STRING_COUNTS: config.SystemProfileSelectionPolicy_SELECT_LAST,
}
// These report types must not set 'system_profile_selection' in the registry.
// The 'system_profile_selection' field must always be set to REPORT_ALL.
// See: //src/bin/config_parser/src/config_parser/expand_defaults.go
var ReportAllOnlyReports = map[config.ReportDefinition_ReportType]bool{
config.ReportDefinition_FLEETWIDE_OCCURRENCE_COUNTS: true,
config.ReportDefinition_FLEETWIDE_HISTOGRAMS: true,
config.ReportDefinition_FLEETWIDE_MEANS: true,
config.ReportDefinition_UNIQUE_DEVICE_NUMERIC_STATS: true,
config.ReportDefinition_HOURLY_VALUE_NUMERIC_STATS: true,
config.ReportDefinition_STRING_COUNTS: true,
}
func validateReportDefinitions(m config.MetricDefinition) error {
reportErrors := newValidationErrors("report")
reportIds := map[uint32]int{}
reportNames := map[string]bool{}
for i, r := range m.Reports {
if _, ok := reportIds[r.Id]; ok {
reportErrors.addError(fmt.Sprintf("Report %d", r.Id), fmt.Errorf("there are two reports with id=%v", r.Id))
continue
}
reportIds[r.Id] = i
if _, ok := reportNames[r.ReportName]; ok {
reportErrors.addError(r.ReportName, fmt.Errorf("there are two reports with name=%v", r.ReportName))
continue
}
reportNames[r.ReportName] = true
if err := validateReportDefinitionForMetric(m, *r); err != nil {
reportErrors.addError(r.ReportName, err)
continue
}
if suggestion, ok := SuggestedSystemProfileSelectionPolicy[r.ReportType]; ok {
if r.SystemProfileSelection == config.SystemProfileSelectionPolicy_SELECT_DEFAULT {
reportErrors.addError(r.ReportName, fmt.Errorf("you must manually specify the system_profile_selection for this report (%v may be a good choice)", suggestion))
}
}
if _, ok := ReportAllOnlyReports[r.ReportType]; ok {
// N.B.: config_parser/expand_defaults.go is run before validation, so SELECT_DEFAULT will get expanded to REPORT_ALL for appropriate metrics.
if r.SystemProfileSelection != config.SystemProfileSelectionPolicy_REPORT_ALL {
reportErrors.addError(r.ReportName, fmt.Errorf("reports of type %v should not manually specify system_profile_selection. The default value is REPORT_ALL", r.ReportType))
}
}
}
return reportErrors.err()
}
// Validate a single instance of a ReportDefinition with its associated metric.
func validateReportDefinitionForMetric(m config.MetricDefinition, r config.ReportDefinition) error {
for _, deletedReportId := range m.DeletedReportIds {
if r.Id == deletedReportId {
return fmt.Errorf("IDs from previously deleted reports are being reused, choose a new report ID for %v", r.Id)
}
}
if err := validateReportType(m.MetricType, r.ReportType); err != nil {
return err
}
if err := validateIntBucketsForReport(m, r); err != nil {
return err
}
if err := validateReportDefinition(m, r); err != nil {
return err
}
return nil
}
// Validate a single instance of a ReportDefinition.
func validateReportDefinition(m config.MetricDefinition, r config.ReportDefinition) error {
if !validNameRegexp.MatchString(r.ReportName) {
return fmt.Errorf("Invalid report name. Report names must match the regular expression '%v'", validNameRegexp)
}
if r.Id == 0 {
return fmt.Errorf("Report ID of zero is not allowed. Please specify a positive report ID.")
}
if err := validateReportDefinitionForType(m, r); err != nil {
return err
}
return nil
}
// Validates that the MetricType and ReportType provided are compatible.
func validateReportType(mt config.MetricDefinition_MetricType, rt config.ReportDefinition_ReportType) error {
if rt == config.ReportDefinition_REPORT_TYPE_UNSET {
return fmt.Errorf("report_type is not set")
}
rts, ok := allowedReportTypes[mt]
if !ok {
return fmt.Errorf("unknown metric type %s", mt)
}
if _, ok = rts[rt]; !ok {
return fmt.Errorf("reports of type %s cannot be used with metrics of type %s", rt, mt)
}
return nil
}
// Validates ReportDefinitions of some types.
func validateReportDefinitionForType(m config.MetricDefinition, r config.ReportDefinition) error {
if reportTypeDeprecatedInCobalt11(r.ReportType) {
return validateReportsDeprecatedInCobalt11(r)
}
// Report types maintained from Cobalt 1.0.
switch r.ReportType {
case config.ReportDefinition_CUSTOM_RAW_DUMP:
return validateReportsMaintainedInCobalt11(r)
}
// All remaining report types are newly added in Cobalt 1.1.
return validateReportsAddedInCobalt11(m, r)
}
// Validate reports with a type added in Cobalt 1.1.
func validateReportsAddedInCobalt11(m config.MetricDefinition, r config.ReportDefinition) error {
reportErrors := newValidationErrors("field")
if err := validateHasNoFieldsDeprecatedInCobalt11(r); err != nil {
reportErrors.addError("deprecated", err)
}
if r.PrivacyLevel == config.ReportDefinition_PRIVACY_LEVEL_UNKNOWN {
reportErrors.addError("privacy_level", fmt.Errorf("The privacy_level field is required for reports of type %s", r.ReportType))
}
if err := validatePoissonFields(r); err != nil {
reportErrors.addError("use_poisson_mechanism_for_privacy or poisson_mean", err)
}
if err := validateMinValueMaxValue(r); err != nil {
reportErrors.addError("min_value or max_value", err)
}
if err := validateMaxCount(r); err != nil {
reportErrors.addError("max_count", err)
}
if err := validateLocalAggregationPeriod(r); err != nil {
reportErrors.addError("local_aggregation_period", err)
}
if err := validateLocalAggregationProcedure(m, r); err != nil {
reportErrors.addError("local_aggregation_procedure", err)
}
if err := validateExpeditedSending(m, r); err != nil {
reportErrors.addError("expedited_sending", err)
}
switch r.ReportType {
case config.ReportDefinition_STRING_COUNTS:
if err := validateStringCountsReportDef(r); err != nil {
reportErrors.addError("other", err)
}
case config.ReportDefinition_UNIQUE_DEVICE_STRING_COUNTS:
if err := validateUniqueDeviceStringCountsReportDef(r); err != nil {
reportErrors.addError("other", err)
}
}
return reportErrors.err()
}
// Validate reports with a type maintained from Cobalt 1.0.
func validateReportsMaintainedInCobalt11(r config.ReportDefinition) error {
if err := validateHasNoFieldsDeprecatedInCobalt11(r); err != nil {
return err
}
if r.PrivacyLevel != config.ReportDefinition_PRIVACY_LEVEL_UNKNOWN {
return fmt.Errorf("The privacy_level field cannot be set for reports of type %s", r.ReportType)
}
return nil
}
// Validate reports with a type deprecated in Cobalt 1.1.
func validateReportsDeprecatedInCobalt11(r config.ReportDefinition) error {
if err := validateHasNoCobalt11OnlyFields(r); err != nil {
return err
}
switch r.ReportType {
case config.ReportDefinition_SIMPLE_OCCURRENCE_COUNT:
return validateSimpleOccurrenceCountReportDef(r)
case config.ReportDefinition_UNIQUE_N_DAY_ACTIVES:
return validateUniqueActivesReportDef(r)
case config.ReportDefinition_PER_DEVICE_NUMERIC_STATS:
return validatePerDeviceNumericStatsReportDef(r)
case config.ReportDefinition_PER_DEVICE_HISTOGRAM:
return validatePerDeviceHistogramReportDef(r)
case config.ReportDefinition_NUMERIC_AGGREGATION:
return validateNumericAggregationReportDef(r)
}
return nil
}
func validateLocalAggregationPeriod(r config.ReportDefinition) error {
switch r.ReportType {
case config.ReportDefinition_UNIQUE_DEVICE_COUNTS,
config.ReportDefinition_UNIQUE_DEVICE_HISTOGRAMS,
config.ReportDefinition_UNIQUE_DEVICE_NUMERIC_STATS,
config.ReportDefinition_UNIQUE_DEVICE_STRING_COUNTS:
if r.LocalAggregationPeriod == config.WindowSize_UNSET {
return fmt.Errorf("day based metrics must specify a local_aggregation_period")
}
case config.ReportDefinition_FLEETWIDE_OCCURRENCE_COUNTS,
config.ReportDefinition_HOURLY_VALUE_HISTOGRAMS,
config.ReportDefinition_FLEETWIDE_HISTOGRAMS,
config.ReportDefinition_FLEETWIDE_MEANS,
config.ReportDefinition_HOURLY_VALUE_NUMERIC_STATS,
config.ReportDefinition_STRING_COUNTS:
if r.LocalAggregationPeriod != config.WindowSize_UNSET {
return fmt.Errorf("hour based metrics must not specify a local_aggregation_period")
}
}
return nil
}
func validateLocalAggregationProcedure(m config.MetricDefinition, r config.ReportDefinition) error {
switch r.ReportType {
case config.ReportDefinition_UNIQUE_DEVICE_COUNTS:
switch r.LocalAggregationProcedure {
case config.ReportDefinition_AT_LEAST_ONCE,
config.ReportDefinition_SELECT_FIRST,
config.ReportDefinition_SELECT_MOST_COMMON:
default:
return fmt.Errorf("reports of type UNIQUE_DEVICE_COUNTS must specify a local_aggregation_procedure in the list (AT_LEAST_ONCE, SELECT_FIRST, SELECT_MOST_COMMON)")
}
case config.ReportDefinition_UNIQUE_DEVICE_HISTOGRAMS,
config.ReportDefinition_HOURLY_VALUE_HISTOGRAMS,
config.ReportDefinition_UNIQUE_DEVICE_NUMERIC_STATS,
config.ReportDefinition_HOURLY_VALUE_NUMERIC_STATS:
if m.MetricType == config.MetricDefinition_INTEGER {
if r.LocalAggregationProcedure == config.ReportDefinition_LOCAL_AGGREGATION_PROCEDURE_UNSET {
return fmt.Errorf("INTEGER metrics with reports of type %v must specify a local_aggregation_procedure", r.ReportType)
}
switch r.LocalAggregationProcedure {
case config.ReportDefinition_SUM_PROCEDURE,
config.ReportDefinition_MIN_PROCEDURE,
config.ReportDefinition_MAX_PROCEDURE,
config.ReportDefinition_MEAN,
config.ReportDefinition_MEDIAN,
config.ReportDefinition_PERCENTILE_N:
default:
return fmt.Errorf("INTEGER metrics with reports of type %v do not support the local_aggregation_procedure %v. Must be one of (SUM_PROCEDURE, MIN_PROCEDURE, MAX_PROCEDURE, MEAN, MEDIAN, PERCENTILE_N)", r.ReportType, r.LocalAggregationProcedure)
}
if r.LocalAggregationProcedure == config.ReportDefinition_PERCENTILE_N {
if r.LocalAggregationProcedurePercentileN == 0 || r.LocalAggregationProcedurePercentileN >= 100 {
return fmt.Errorf("metrics with PERCENTILE_N local_aggregation_procedure must set local_aggregation_procedure_percentile_n in (0, 100)")
}
}
} else if m.MetricType == config.MetricDefinition_OCCURRENCE {
if r.LocalAggregationProcedure != config.ReportDefinition_LOCAL_AGGREGATION_PROCEDURE_UNSET {
return fmt.Errorf("OCCURRENCE metrics with reports of type %v do not support local_aggregation_procedure", r.ReportType)
}
}
default:
if r.LocalAggregationProcedure != config.ReportDefinition_LOCAL_AGGREGATION_PROCEDURE_UNSET {
return fmt.Errorf("reports of type %v do not support local_aggregation_procedure", r.ReportType)
}
}
if r.LocalAggregationProcedure == config.ReportDefinition_SELECT_FIRST && r.EventVectorBufferMax > 1 {
return fmt.Errorf("reports with local_aggregation_procedure: SELECT_FIRST should not set event_vector_buffer_max to a value other than 1 (the default)")
}
return nil
}
func validateExpeditedSending(m config.MetricDefinition, r config.ReportDefinition) error {
if !r.ExpeditedSending {
return nil
}
switch r.ReportType {
case config.ReportDefinition_UNIQUE_DEVICE_COUNTS:
switch r.LocalAggregationProcedure {
case config.ReportDefinition_AT_LEAST_ONCE,
config.ReportDefinition_SELECT_FIRST:
default:
return fmt.Errorf("reports of type UNIQUE_DEVICE_COUNTS with expedited_sending enabled must specify a local_aggregation_procedure in the list (AT_LEAST_ONCE, SELECT_FIRST)")
}
case config.ReportDefinition_UNIQUE_DEVICE_STRING_COUNTS:
default:
return fmt.Errorf("reports of type %v do not support expedited_sending", r.ReportType)
}
if r.PrivacyLevel != config.ReportDefinition_NO_ADDED_PRIVACY {
return fmt.Errorf("The expedited_sending can not be enabled unless the privacy_level field is set to NO_ADDED_PRIVACY")
}
return nil
}
/////////////////////////////////////////////////////////////////
// Validation for specific report types:
/////////////////////////////////////////////////////////////////
func validateSimpleOccurrenceCountReportDef(r config.ReportDefinition) error {
if err := validateLocalPrivacyNoiseLevel(r); err != nil {
return err
}
return nil
}
func validateUniqueActivesReportDef(r config.ReportDefinition) error {
if err := validateWindowSize(r); err != nil {
return err
}
if err := validateLocalPrivacyNoiseLevel(r); err != nil {
return err
}
return nil
}
func validatePerDeviceNumericStatsReportDef(r config.ReportDefinition) error {
if err := validateWindowSize(r); err != nil {
return err
}
return nil
}
func validatePerDeviceHistogramReportDef(r config.ReportDefinition) error {
if r.IntBuckets == nil {
return fmt.Errorf("No int_buckets specified for report of type %s.", r.ReportType)
}
if err := validateWindowSize(r); err != nil {
return err
}
return nil
}
func validateNumericAggregationReportDef(r config.ReportDefinition) error {
if err := validatePercentiles(r); err != nil {
return err
}
return nil
}
func validateStringCountsReportDef(r config.ReportDefinition) error {
if r.IntBuckets != nil {
return fmt.Errorf("int_buckets can not be specified for report of type %s.", r.ReportType)
}
if r.StringBufferMax == 0 {
return fmt.Errorf("no string_buffer_max specified for report of type %s.", r.ReportType)
}
return nil
}
func validateUniqueDeviceStringCountsReportDef(r config.ReportDefinition) error {
if r.IntBuckets != nil {
return fmt.Errorf("int_buckets can not be specified for report of type %s.", r.ReportType)
}
if r.StringBufferMax == 0 {
return fmt.Errorf("no string_buffer_max specified for report of type %s.", r.ReportType)
}
return nil
}
/////////////////////////////////////////////////////////////////
// Validation for specific fields of report definitions
//
// These functions are oblivious to the report's type and should
// only be called with reports for which that field is required.
/////////////////////////////////////////////////////////////////
// Check that the report definition has a nonempty window_size field, and that none of the window sizes
// are UNSET.
func validateWindowSize(r config.ReportDefinition) error {
if len(r.WindowSize) == 0 {
return fmt.Errorf("No window_size specified for report of type %s.", r.ReportType)
}
for _, ws := range r.WindowSize {
if ws == config.WindowSize_UNSET {
return fmt.Errorf("Unset window size found for report of type %s.", r.ReportType)
}
}
return nil
}
// Check that the local_privacy_noise_level field is set.
func validateLocalPrivacyNoiseLevel(r config.ReportDefinition) error {
if r.LocalPrivacyNoiseLevel == config.ReportDefinition_NOISE_LEVEL_UNSET {
return fmt.Errorf("No local_privacy_noise_level specified for report of type %s.", r.ReportType)
}
return nil
}
func validatePoissonFields(r config.ReportDefinition) error {
if r.UsePoissonMechanismForPrivacy {
if r.PrivacyLevel == config.ReportDefinition_NO_ADDED_PRIVACY {
return fmt.Errorf("use_poisson_mechanism_for_privacy should only be set when privacy_level is not NO_ADDED_PRIVACY")
}
if r.PoissonMean == 0.0 {
return fmt.Errorf("poisson_mean must be set when use_poisson_mechanism_for_privacy is selected")
}
if r.PoissonMean < 0.0 {
return fmt.Errorf("poisson_mean should not be negative")
}
} else {
if r.PoissonMean != 0.0 {
return fmt.Errorf("poisson_mean should not be set when use_poisson_mechanism_for_privacy is not selected")
}
}
return nil
}
// Check that the percentiles are between 0 and 100.
func validatePercentiles(r config.ReportDefinition) error {
for _, percentage := range r.Percentiles {
if percentage < 0 || percentage > 100 {
return fmt.Errorf(
"Percentiles must be in the range 0-100. Received value %d for report of type %s.",
percentage, r.ReportType)
}
}
return nil
}
func validateMinValueMaxValue(r config.ReportDefinition) error {
switch r.PrivacyLevel {
case
config.ReportDefinition_PRIVACY_LEVEL_UNKNOWN:
return nil
case
config.ReportDefinition_NO_ADDED_PRIVACY:
if r.MaxValue != 0 || r.MinValue != 0 {
return fmt.Errorf("min_value and max_value should not be set for reports with no added privacy")
}
case
config.ReportDefinition_LOW_PRIVACY,
config.ReportDefinition_MEDIUM_PRIVACY,
config.ReportDefinition_HIGH_PRIVACY:
switch r.ReportType {
case
config.ReportDefinition_FLEETWIDE_OCCURRENCE_COUNTS,
config.ReportDefinition_UNIQUE_DEVICE_NUMERIC_STATS,
config.ReportDefinition_HOURLY_VALUE_NUMERIC_STATS,
config.ReportDefinition_FLEETWIDE_MEANS:
if r.MaxValue == 0 && r.MinValue == 0 {
return fmt.Errorf("min_value and max_value must be set for reports of type %s", r.ReportType)
}
if r.MaxValue < r.MinValue {
return fmt.Errorf("min_value must be less than or equal to max_value: %v > %v", r.MinValue, r.MaxValue)
}
case
config.ReportDefinition_UNIQUE_DEVICE_COUNTS,
config.ReportDefinition_FLEETWIDE_HISTOGRAMS,
config.ReportDefinition_UNIQUE_DEVICE_HISTOGRAMS,
config.ReportDefinition_HOURLY_VALUE_HISTOGRAMS,
config.ReportDefinition_STRING_COUNTS,
config.ReportDefinition_UNIQUE_DEVICE_STRING_COUNTS:
if r.MinValue != 0 || r.MaxValue != 0 {
return fmt.Errorf("min_value and max_value should not be set for reports of type %s", r.ReportType)
}
default:
return fmt.Errorf("unrecognized report type %s", r.ReportType)
}
}
return nil
}
func validateMaxCount(r config.ReportDefinition) error {
switch r.PrivacyLevel {
case
config.ReportDefinition_PRIVACY_LEVEL_UNKNOWN:
return nil
case
config.ReportDefinition_NO_ADDED_PRIVACY:
if r.MaxCount != 0 {
return fmt.Errorf("max_count should not be set for reports with no added privacy")
}
case
config.ReportDefinition_LOW_PRIVACY,
config.ReportDefinition_MEDIUM_PRIVACY,
config.ReportDefinition_HIGH_PRIVACY:
switch r.ReportType {
case
config.ReportDefinition_FLEETWIDE_HISTOGRAMS,
config.ReportDefinition_STRING_COUNTS,
config.ReportDefinition_FLEETWIDE_MEANS:
if r.MaxCount == 0 {
return fmt.Errorf("max_count must be set for reports of type %s", r.ReportType)
}
case
config.ReportDefinition_UNIQUE_DEVICE_COUNTS,
config.ReportDefinition_FLEETWIDE_OCCURRENCE_COUNTS,
config.ReportDefinition_UNIQUE_DEVICE_NUMERIC_STATS,
config.ReportDefinition_HOURLY_VALUE_NUMERIC_STATS,
config.ReportDefinition_UNIQUE_DEVICE_HISTOGRAMS,
config.ReportDefinition_HOURLY_VALUE_HISTOGRAMS,
config.ReportDefinition_UNIQUE_DEVICE_STRING_COUNTS:
if r.MaxCount != 0 {
return fmt.Errorf("max_count should not be set for reports of type %s", r.ReportType)
}
default:
return fmt.Errorf("unrecognized report type %s", r.ReportType)
}
}
return nil
}
func validateHasNoCobalt11OnlyFields(r config.ReportDefinition) error {
rt := r.ReportType
if r.PrivacyLevel != config.ReportDefinition_PRIVACY_LEVEL_UNKNOWN {
return fmt.Errorf("The privacy_level field cannot be set for reports of type %s", rt)
}
if r.MinValue != 0 || r.MaxValue != 0 || r.MaxCount != 0 {
return fmt.Errorf("The min_value, max_value and max_count field cannot be set for reports of type %s", rt)
}
if r.LocalAggregationProcedure != config.ReportDefinition_LOCAL_AGGREGATION_PROCEDURE_UNSET {
return fmt.Errorf("The local_aggregation_procedure field cannot be set for reports of type %s", rt)
}
if r.LocalAggregationProcedurePercentileN != 0 {
return fmt.Errorf("The field local_aggregation_procedure_percentile_n cannot be set for reports of type %s", rt)
}
if r.LocalAggregationPeriod != config.WindowSize_UNSET {
return fmt.Errorf("The local_aggregation_period field cannot be set for reports of type %s", rt)
}
if r.ReportingThreshold != 0 {
return fmt.Errorf("the reporting_threshold filed cannot be set for reports of type %s", rt)
}
return nil
}
func validateHasNoFieldsDeprecatedInCobalt11(r config.ReportDefinition) error {
rt := r.ReportType
if r.LocalPrivacyNoiseLevel != config.ReportDefinition_NOISE_LEVEL_UNSET {
return fmt.Errorf("The local_privacy_noise_level field cannot be set for reports of type %s", rt)
}
if r.CandidateList != nil && len(r.CandidateList) != 0 {
return fmt.Errorf("The candidate_list field cannot be set for reports of type %s", rt)
}
if len(r.WindowSize) != 0 {
return fmt.Errorf("The window_size field cannot be set for reports of type %s", rt)
}
if len(r.AggregationWindow) != 0 {
return fmt.Errorf("The aggregation_window field cannot be set for reports of type %s", rt)
}
return nil
}