| // 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 |
| } |