| 157 | } |
| 158 | |
| 159 | func ValidateCostAttributionDimensions(dimensions map[string]string) error { |
| 160 | seenLabels := make(map[string]string) |
| 161 | |
| 162 | // map is with key=tempo attribute, value=prometheus labelName |
| 163 | for k, v := range dimensions { |
| 164 | // build labelName in the similar way as usage.GetBuffersForDimensions |
| 165 | attr, _ := usage.ParseDimensionKey(k) // extract attr so validate the duplicates with scope prefix |
| 166 | labelName := v |
| 167 | if labelName == "" { |
| 168 | labelName = attr // The dimension is using default mapping, we map it to attribute |
| 169 | } |
| 170 | labelName = strutil.SanitizeFullLabelName(labelName) // sanitize label name |
| 171 | |
| 172 | // check for duplicate prometheus label names. |
| 173 | // when we have duplicate labelNames, we randomly pick one so validate and don't allow duplicates. |
| 174 | if originalKey, exists := seenLabels[labelName]; exists { |
| 175 | return fmt.Errorf("cost_attribution.dimensions has duplicate label name: '%s', both '%s' and '%s' map to it", labelName, originalKey, k) |
| 176 | } |
| 177 | seenLabels[labelName] = k // put k as value so we can show configured keys in the error |
| 178 | |
| 179 | // creating a desc do the complete labelName validation |
| 180 | desc := prometheus.NewDesc("test_desc", "test desc created for validation", []string{labelName}, nil) |
| 181 | // try to create a metric and see if there are any error, we use same method in usage.Collect |
| 182 | _, err := prometheus.NewConstMetric(desc, prometheus.CounterValue, float64(1), labelName) |
| 183 | if err != nil { |
| 184 | return fmt.Errorf("cost_attribution.dimensions config has invalid label name: '%s'", labelName) |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | // no errors, we are good. |
| 189 | return nil |
| 190 | } |
| 191 | |
| 192 | func ValidateIntrinsicDimensions(intrinsicDimensions map[string]bool) error { |
| 193 | for dim := range intrinsicDimensions { |