master
go 154 lines 3.43 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package secretstore
4
5 import (
6 "fmt"
7 "strings"
8
9 "github.com/netdata/netdata/go/plugins/plugin/framework/confgroup"
10
11 "github.com/gohugoio/hashstructure"
12 )
13
14 const (
15 keyName = "name"
16 keyKind = "kind"
17
18 ikeySource = "__source__"
19 ikeySourceType = "__source_type__"
20 )
21
22 // Config is the external raw secretstore object shape used by dyncfg/cache layers.
23 // It carries structural metadata alongside provider payload and excludes __ metadata
24 // from content hashing.
25 type Config map[string]any
26
27 func (c Config) Set(key string, value any) Config { c[key] = value; return c }
28
29 func StoreKey(kind StoreKind, name string) string {
30 kind = StoreKind(strings.TrimSpace(string(kind)))
31 name = strings.TrimSpace(name)
32 if kind == "" || name == "" {
33 return ""
34 }
35 return string(kind) + ":" + name
36 }
37
38 func ParseStoreKey(key string) (StoreKind, string, error) {
39 key = strings.TrimSpace(key)
40 if key == "" {
41 return "", "", fmt.Errorf("store key is required")
42 }
43
44 kindPart, name, ok := strings.Cut(key, ":")
45 if !ok {
46 return "", "", fmt.Errorf("store key '%s' must be in format 'kind:name'", key)
47 }
48
49 kind := StoreKind(strings.TrimSpace(kindPart))
50 name = strings.TrimSpace(name)
51 if !kind.IsValid() {
52 return "", "", fmt.Errorf("invalid store kind '%s'", kind)
53 }
54 if err := validateStoreName(name); err != nil {
55 return "", "", fmt.Errorf("invalid store name '%s': %w", name, err)
56 }
57
58 return kind, name, nil
59 }
60
61 func (c Config) Name() string {
62 v, _ := c[keyName].(string)
63 return v
64 }
65
66 func (c Config) Kind() StoreKind {
67 switch v := c[keyKind].(type) {
68 case StoreKind:
69 return v
70 case string:
71 return StoreKind(v)
72 default:
73 return ""
74 }
75 }
76
77 func (c Config) Source() string {
78 v, _ := c[ikeySource].(string)
79 return v
80 }
81
82 func (c Config) SourceType() string {
83 v, _ := c[ikeySourceType].(string)
84 return v
85 }
86
87 func (c Config) SetName(v string) Config { return c.Set(keyName, v) }
88 func (c Config) SetKind(v StoreKind) Config { return c.Set(keyKind, string(v)) }
89 func (c Config) SetSource(v string) Config { return c.Set(ikeySource, v) }
90 func (c Config) SetSourceType(v string) Config { return c.Set(ikeySourceType, v) }
91
92 func (c Config) ExposedKey() string {
93 return StoreKey(c.Kind(), c.Name())
94 }
95
96 func (c Config) UID() string {
97 if c.Source() == "" || c.ExposedKey() == "" {
98 return ""
99 }
100 return c.Source() + ":" + c.ExposedKey()
101 }
102
103 func (c Config) SourceTypePriority() int {
104 switch c.SourceType() {
105 case confgroup.TypeDyncfg:
106 return 16
107 case confgroup.TypeUser:
108 return 8
109 case confgroup.TypeStock:
110 return 2
111 default:
112 return 0
113 }
114 }
115
116 func (c Config) HashIncludeMap(_ string, k, _ any) (bool, error) {
117 s := k.(string)
118 return !strings.HasPrefix(s, "__") && !strings.HasSuffix(s, "__"), nil
119 }
120
121 func (c Config) Hash() uint64 {
122 hash, _ := hashstructure.Hash(c, nil)
123 return hash
124 }
125
126 func (c Config) Validate() error {
127 if c == nil {
128 return fmt.Errorf("store config is nil")
129 }
130
131 name := c.Name()
132 if name == "" {
133 return fmt.Errorf("store name is required")
134 }
135 if err := validateStoreName(name); err != nil {
136 return fmt.Errorf("invalid store name '%s': %w", name, err)
137 }
138
139 kind := c.Kind()
140 if !kind.IsValid() {
141 return fmt.Errorf("invalid store kind '%s'", kind)
142 }
143
144 if c.Source() == "" {
145 return fmt.Errorf("store source is required")
146 }
147
148 switch c.SourceType() {
149 case confgroup.TypeDyncfg, confgroup.TypeUser, confgroup.TypeStock:
150 return nil
151 default:
152 return fmt.Errorf("invalid store source type '%s'", c.SourceType())
153 }
154 }