config.go (3626B)
1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // package config provides methods for loading and querying a 6 // telemetry upload config file. 7 package config 8 9 import ( 10 "encoding/json" 11 "os" 12 "strings" 13 14 "golang.org/x/telemetry/internal/telemetry" 15 ) 16 17 // Config is a wrapper around telemetry.UploadConfig that provides some 18 // convenience methods for checking the contents of a report. 19 type Config struct { 20 *telemetry.UploadConfig 21 program map[string]bool 22 goos map[string]bool 23 goarch map[string]bool 24 goversion map[string]bool 25 pgversion map[pgkey]bool 26 pgcounter map[pgkey]bool 27 pgcounterprefix map[pgkey]bool 28 pgstack map[pgkey]bool 29 rate map[pgkey]float64 30 } 31 32 type pgkey struct { 33 program, key string 34 } 35 36 func ReadConfig(file string) (*Config, error) { 37 data, err := os.ReadFile(file) 38 if err != nil { 39 return nil, err 40 } 41 var cfg telemetry.UploadConfig 42 if err := json.Unmarshal(data, &cfg); err != nil { 43 return nil, err 44 } 45 return NewConfig(&cfg), nil 46 } 47 48 func NewConfig(cfg *telemetry.UploadConfig) *Config { 49 ucfg := Config{UploadConfig: cfg} 50 ucfg.goos = set(ucfg.GOOS) 51 ucfg.goarch = set(ucfg.GOARCH) 52 ucfg.goversion = set(ucfg.GoVersion) 53 ucfg.program = make(map[string]bool, len(ucfg.Programs)) 54 ucfg.pgversion = make(map[pgkey]bool, len(ucfg.Programs)) 55 ucfg.pgcounter = make(map[pgkey]bool, len(ucfg.Programs)) 56 ucfg.pgcounterprefix = make(map[pgkey]bool, len(ucfg.Programs)) 57 ucfg.pgstack = make(map[pgkey]bool, len(ucfg.Programs)) 58 ucfg.rate = make(map[pgkey]float64) 59 for _, p := range ucfg.Programs { 60 ucfg.program[p.Name] = true 61 for _, v := range p.Versions { 62 ucfg.pgversion[pgkey{p.Name, v}] = true 63 } 64 for _, c := range p.Counters { 65 for _, e := range Expand(c.Name) { 66 ucfg.pgcounter[pgkey{p.Name, e}] = true 67 ucfg.rate[pgkey{p.Name, e}] = c.Rate 68 } 69 prefix, _, found := strings.Cut(c.Name, ":") 70 if found { 71 ucfg.pgcounterprefix[pgkey{p.Name, prefix}] = true 72 } 73 } 74 for _, s := range p.Stacks { 75 ucfg.pgstack[pgkey{p.Name, s.Name}] = true 76 ucfg.rate[pgkey{p.Name, s.Name}] = s.Rate 77 } 78 } 79 return &ucfg 80 } 81 82 func (r *Config) HasProgram(s string) bool { 83 return r.program[s] 84 } 85 86 func (r *Config) HasGOOS(s string) bool { 87 return r.goos[s] 88 } 89 90 func (r *Config) HasGOARCH(s string) bool { 91 return r.goarch[s] 92 } 93 94 func (r *Config) HasGoVersion(s string) bool { 95 return r.goversion[s] 96 } 97 98 func (r *Config) HasVersion(program, version string) bool { 99 return r.pgversion[pgkey{program, version}] 100 } 101 102 func (r *Config) HasCounter(program, counter string) bool { 103 return r.pgcounter[pgkey{program, counter}] 104 } 105 106 func (r *Config) HasCounterPrefix(program, prefix string) bool { 107 return r.pgcounterprefix[pgkey{program, prefix}] 108 } 109 110 func (r *Config) HasStack(program, stack string) bool { 111 return r.pgstack[pgkey{program, stack}] 112 } 113 114 func (r *Config) Rate(program, name string) float64 { 115 return r.rate[pgkey{program, name}] 116 } 117 118 func set(slice []string) map[string]bool { 119 s := make(map[string]bool, len(slice)) 120 for _, v := range slice { 121 s[v] = true 122 } 123 return s 124 } 125 126 // Expand takes a counter defined with buckets and expands it into distinct 127 // strings for each bucket. 128 func Expand(counter string) []string { 129 prefix, rest, hasBuckets := strings.Cut(counter, "{") 130 var counters []string 131 if hasBuckets { 132 buckets := strings.Split(strings.TrimSuffix(rest, "}"), ",") 133 for _, b := range buckets { 134 counters = append(counters, prefix+b) 135 } 136 } else { 137 counters = append(counters, prefix) 138 } 139 return counters 140 }