github.com/influxdata/telegraf@v1.30.3/config/plugin_id.go (about) 1 package config 2 3 import ( 4 "crypto/sha256" 5 "encoding/hex" 6 "fmt" 7 "sort" 8 9 "github.com/influxdata/toml/ast" 10 ) 11 12 type keyValuePair struct { 13 Key string 14 Value string 15 } 16 17 func processTable(parent string, table *ast.Table) ([]keyValuePair, error) { 18 var prefix string 19 var options []keyValuePair 20 21 if parent != "" { 22 prefix = parent + "." 23 } 24 25 for k, value := range table.Fields { 26 switch v := value.(type) { 27 case *ast.KeyValue: 28 key := prefix + k 29 options = append(options, keyValuePair{ 30 Key: key, 31 Value: v.Value.Source(), 32 }) 33 case *ast.Table: 34 key := prefix + k 35 childs, err := processTable(key, v) 36 if err != nil { 37 return nil, fmt.Errorf("parsing table for %q failed: %w", key, err) 38 } 39 options = append(options, childs...) 40 case []*ast.Table: 41 for i, t := range v { 42 key := fmt.Sprintf("%s#%d.%s", prefix, i, k) 43 childs, err := processTable(key, t) 44 if err != nil { 45 return nil, fmt.Errorf("parsing table for %q #%d failed: %w", key, i, err) 46 } 47 options = append(options, childs...) 48 } 49 default: 50 return nil, fmt.Errorf("unknown node type %T in key %q", value, prefix+k) 51 } 52 } 53 return options, nil 54 } 55 56 func generatePluginID(prefix string, table *ast.Table) (string, error) { 57 // We need to ensure that identically configured plugins _always_ 58 // result in the same ID no matter which order the options are specified. 59 // This is even more relevant as Golang does _not_ give any guarantee 60 // on the ordering of maps. 61 // So we flatten out the configuration options (also for nested objects) 62 // and then sort the resulting array by the canonical key-name. 63 cfg, err := processTable("", table) 64 if err != nil { 65 return "", fmt.Errorf("processing AST failed: %w", err) 66 } 67 sort.SliceStable(cfg, func(i, j int) bool { return cfg[i].Key < cfg[j].Key }) 68 69 // Hash the config options to get the ID. We also prefix the ID with 70 // the plugin name to prevent overlap with other plugin types. 71 hash := sha256.New() 72 hash.Write(append([]byte(prefix), 0)) 73 for _, kv := range cfg { 74 hash.Write([]byte(kv.Key + ":" + kv.Value)) 75 hash.Write([]byte{0}) 76 } 77 78 return hex.EncodeToString(hash.Sum(nil)), nil 79 }