| // Copyright 2023 The Go 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 provides methods for loading and querying a | |
| // telemetry upload config file. | |
| package config | |
| import ( | |
| "encoding/json" | |
| "os" | |
| "strings" | |
| "golang.org/x/telemetry/internal/telemetry" | |
| ) | |
| // Config is a wrapper around telemetry.UploadConfig that provides some | |
| // convenience methods for checking the contents of a report. | |
| type Config struct { | |
| *telemetry.UploadConfig | |
| program map[string]bool | |
| goos map[string]bool | |
| goarch map[string]bool | |
| goversion map[string]bool | |
| pgversion map[pgkey]bool | |
| pgcounter map[pgkey]bool | |
| pgcounterprefix map[pgkey]bool | |
| pgstack map[pgkey]bool | |
| rate map[pgkey]float64 | |
| } | |
| type pgkey struct { | |
| program, key string | |
| } | |
| func ReadConfig(file string) (*Config, error) { | |
| data, err := os.ReadFile(file) | |
| if err != nil { | |
| return nil, err | |
| } | |
| var cfg telemetry.UploadConfig | |
| if err := json.Unmarshal(data, &cfg); err != nil { | |
| return nil, err | |
| } | |
| return NewConfig(&cfg), nil | |
| } | |
| func NewConfig(cfg *telemetry.UploadConfig) *Config { | |
| ucfg := Config{UploadConfig: cfg} | |
| ucfg.goos = set(ucfg.GOOS) | |
| ucfg.goarch = set(ucfg.GOARCH) | |
| ucfg.goversion = set(ucfg.GoVersion) | |
| ucfg.program = make(map[string]bool, len(ucfg.Programs)) | |
| ucfg.pgversion = make(map[pgkey]bool, len(ucfg.Programs)) | |
| ucfg.pgcounter = make(map[pgkey]bool, len(ucfg.Programs)) | |
| ucfg.pgcounterprefix = make(map[pgkey]bool, len(ucfg.Programs)) | |
| ucfg.pgstack = make(map[pgkey]bool, len(ucfg.Programs)) | |
| ucfg.rate = make(map[pgkey]float64) | |
| for _, p := range ucfg.Programs { | |
| ucfg.program[p.Name] = true | |
| for _, v := range p.Versions { | |
| ucfg.pgversion[pgkey{p.Name, v}] = true | |
| } | |
| for _, c := range p.Counters { | |
| for _, e := range Expand(c.Name) { | |
| ucfg.pgcounter[pgkey{p.Name, e}] = true | |
| ucfg.rate[pgkey{p.Name, e}] = c.Rate | |
| } | |
| prefix, _, found := strings.Cut(c.Name, ":") | |
| if found { | |
| ucfg.pgcounterprefix[pgkey{p.Name, prefix}] = true | |
| } | |
| } | |
| for _, s := range p.Stacks { | |
| ucfg.pgstack[pgkey{p.Name, s.Name}] = true | |
| ucfg.rate[pgkey{p.Name, s.Name}] = s.Rate | |
| } | |
| } | |
| return &ucfg | |
| } | |
| func (r *Config) HasProgram(s string) bool { | |
| return r.program[s] | |
| } | |
| func (r *Config) HasGOOS(s string) bool { | |
| return r.goos[s] | |
| } | |
| func (r *Config) HasGOARCH(s string) bool { | |
| return r.goarch[s] | |
| } | |
| func (r *Config) HasGoVersion(s string) bool { | |
| return r.goversion[s] | |
| } | |
| func (r *Config) HasVersion(program, version string) bool { | |
| return r.pgversion[pgkey{program, version}] | |
| } | |
| func (r *Config) HasCounter(program, counter string) bool { | |
| return r.pgcounter[pgkey{program, counter}] | |
| } | |
| func (r *Config) HasCounterPrefix(program, prefix string) bool { | |
| return r.pgcounterprefix[pgkey{program, prefix}] | |
| } | |
| func (r *Config) HasStack(program, stack string) bool { | |
| return r.pgstack[pgkey{program, stack}] | |
| } | |
| func (r *Config) Rate(program, name string) float64 { | |
| return r.rate[pgkey{program, name}] | |
| } | |
| func set(slice []string) map[string]bool { | |
| s := make(map[string]bool, len(slice)) | |
| for _, v := range slice { | |
| s[v] = true | |
| } | |
| return s | |
| } | |
| // Expand takes a counter defined with buckets and expands it into distinct | |
| // strings for each bucket. | |
| func Expand(counter string) []string { | |
| prefix, rest, hasBuckets := strings.Cut(counter, "{") | |
| var counters []string | |
| if hasBuckets { | |
| buckets := strings.Split(strings.TrimSuffix(rest, "}"), ",") | |
| for _, b := range buckets { | |
| counters = append(counters, prefix+b) | |
| } | |
| } else { | |
| counters = append(counters, prefix) | |
| } | |
| return counters | |
| } |