| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | package confgroup |
| 4 | |
| 5 | import ( |
| 6 | "fmt" |
| 7 | "regexp" |
| 8 | "strings" |
| 9 | |
| 10 | "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi" |
| 11 | |
| 12 | "github.com/gohugoio/hashstructure" |
| 13 | "gopkg.in/yaml.v2" |
| 14 | ) |
| 15 | |
| 16 | const ( |
| 17 | keyName = "name" |
| 18 | keyModule = "module" |
| 19 | keyUpdateEvery = "update_every" |
| 20 | keyDetectRetry = "autodetection_retry" |
| 21 | keyPriority = "priority" |
| 22 | keyLabels = "labels" |
| 23 | keyVnode = "vnode" |
| 24 | keyFunctionOnly = "function_only" |
| 25 | |
| 26 | ikeySource = "__source__" |
| 27 | ikeySourceType = "__source_type__" |
| 28 | ikeyProvider = "__provider__" |
| 29 | ) |
| 30 | |
| 31 | const ( |
| 32 | TypeStock = "stock" |
| 33 | TypeUser = "user" |
| 34 | TypeDiscovered = "discovered" |
| 35 | TypeDyncfg = "dyncfg" |
| 36 | ) |
| 37 | |
| 38 | type Config map[string]any |
| 39 | |
| 40 | func (c Config) HashIncludeMap(_ string, k, _ any) (bool, error) { |
| 41 | s := k.(string) |
| 42 | return !strings.HasPrefix(s, "__") && !strings.HasSuffix(s, "__"), nil |
| 43 | } |
| 44 | |
| 45 | func (c Config) Set(key string, value any) Config { c[key] = value; return c } |
| 46 | func (c Config) Get(key string) any { return c[key] } |
| 47 | |
| 48 | func (c Config) Name() string { v, _ := c.Get(keyName).(string); return v } |
| 49 | func (c Config) Module() string { v, _ := c.Get(keyModule).(string); return v } |
| 50 | func (c Config) FullName() string { return fullName(c.Name(), c.Module()) } |
| 51 | func (c Config) ExposedKey() string { return c.FullName() } |
| 52 | func (c Config) UpdateEvery() int { v, _ := c.Get(keyUpdateEvery).(int); return v } |
| 53 | func (c Config) AutoDetectionRetry() int { v, _ := c.Get(keyDetectRetry).(int); return v } |
| 54 | func (c Config) Priority() int { v, _ := c.Get(keyPriority).(int); return v } |
| 55 | func (c Config) Labels() map[any]any { v, _ := c.Get(keyLabels).(map[any]any); return v } |
| 56 | func (c Config) Hash() uint64 { return calcHash(c) } |
| 57 | func (c Config) Vnode() string { v, _ := c.Get(keyVnode).(string); return v } |
| 58 | func (c Config) FunctionOnly() bool { v, _ := c.Get(keyFunctionOnly).(bool); return v } |
| 59 | |
| 60 | func (c Config) SetName(v string) Config { return c.Set(keyName, v) } |
| 61 | func (c Config) SetModule(v string) Config { return c.Set(keyModule, v) } |
| 62 | |
| 63 | func (c Config) UID() string { |
| 64 | return fmt.Sprintf("%s_%s_%s_%s_%d", c.SourceType(), c.Provider(), c.Source(), c.FullName(), c.Hash()) |
| 65 | } |
| 66 | |
| 67 | func (c Config) Source() string { v, _ := c.Get(ikeySource).(string); return v } |
| 68 | func (c Config) SourceType() string { v, _ := c.Get(ikeySourceType).(string); return v } |
| 69 | func (c Config) Provider() string { v, _ := c.Get(ikeyProvider).(string); return v } |
| 70 | func (c Config) SetSource(v string) Config { return c.Set(ikeySource, v) } |
| 71 | func (c Config) SetSourceType(v string) Config { return c.Set(ikeySourceType, v) } |
| 72 | func (c Config) SetProvider(v string) Config { return c.Set(ikeyProvider, v) } |
| 73 | |
| 74 | func (c Config) SourceTypePriority() int { |
| 75 | switch c.SourceType() { |
| 76 | default: |
| 77 | return 0 |
| 78 | case TypeStock: |
| 79 | return 2 |
| 80 | case TypeDiscovered: |
| 81 | return 4 |
| 82 | case TypeUser: |
| 83 | return 8 |
| 84 | case TypeDyncfg: |
| 85 | return 16 |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | func (c Config) Clone() (Config, error) { |
| 90 | type plain Config |
| 91 | bytes, err := yaml.Marshal((plain)(c)) |
| 92 | if err != nil { |
| 93 | return nil, err |
| 94 | } |
| 95 | var newConfig Config |
| 96 | if err := yaml.Unmarshal(bytes, &newConfig); err != nil { |
| 97 | return nil, err |
| 98 | } |
| 99 | return newConfig, nil |
| 100 | } |
| 101 | |
| 102 | func (c Config) ApplyDefaults(def Default) { |
| 103 | if c.UpdateEvery() <= 0 { |
| 104 | v := firstPositive(def.UpdateEvery, collectorapi.UpdateEvery) |
| 105 | c.Set("update_every", v) |
| 106 | } |
| 107 | if c.AutoDetectionRetry() <= 0 { |
| 108 | v := firstPositive(def.AutoDetectionRetry, collectorapi.AutoDetectionRetry) |
| 109 | c.Set("autodetection_retry", v) |
| 110 | } |
| 111 | if c.Priority() <= 0 { |
| 112 | v := firstPositive(def.Priority, collectorapi.Priority) |
| 113 | c.Set("priority", v) |
| 114 | } |
| 115 | if c.UpdateEvery() < def.MinUpdateEvery && def.MinUpdateEvery > 0 { |
| 116 | c.Set("update_every", def.MinUpdateEvery) |
| 117 | } |
| 118 | if c.Name() == "" { |
| 119 | c.Set("name", c.Module()) |
| 120 | } else { |
| 121 | c.Set("name", cleanName(c.Name())) |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | var reInvalidCharacters = regexp.MustCompile(`\s+|\.+|:+`) |
| 126 | |
| 127 | func cleanName(name string) string { |
| 128 | return reInvalidCharacters.ReplaceAllString(name, "_") |
| 129 | } |
| 130 | |
| 131 | func fullName(name, module string) string { |
| 132 | if name == module { |
| 133 | return name |
| 134 | } |
| 135 | return module + "_" + name |
| 136 | } |
| 137 | |
| 138 | func calcHash(obj any) uint64 { |
| 139 | hash, _ := hashstructure.Hash(obj, nil) |
| 140 | return hash |
| 141 | } |
| 142 | |
| 143 | func firstPositive(value int, others ...int) int { |
| 144 | if value > 0 || len(others) == 0 { |
| 145 | return value |
| 146 | } |
| 147 | return firstPositive(others[0], others[1:]...) |
| 148 | } |