master
go 166 lines 4.33 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package secretsctl
4
5 import (
6 "fmt"
7
8 "github.com/netdata/netdata/go/plugins/logger"
9 "github.com/netdata/netdata/go/plugins/pkg/netdataapi"
10 "github.com/netdata/netdata/go/plugins/plugin/agent/secrets/secretstore"
11 "github.com/netdata/netdata/go/plugins/plugin/framework/dyncfg"
12 )
13
14 const (
15 dyncfgSecretStorePrefixf = "%s:secretstore:"
16 dyncfgSecretStorePath = "/collectors/%s/SecretStores"
17 )
18
19 type Options struct {
20 Logger *logger.Logger
21 API *dyncfg.Responder
22 Seen *dyncfg.SeenCache[secretstore.Config]
23 Exposed *dyncfg.ExposedCache[secretstore.Config]
24 Service secretstore.Service
25 Plugin string
26 Initial []secretstore.Config
27
28 AffectedJobs func(string) []secretstore.JobRef
29 RestartableAffectedJobs func(string) []secretstore.JobRef
30 RestartDependentJobs func(string) string
31 }
32
33 type Entry struct {
34 Cfg secretstore.Config
35 Status dyncfg.Status
36 }
37
38 type Controller struct {
39 *logger.Logger
40
41 api *dyncfg.Responder
42 seen *dyncfg.SeenCache[secretstore.Config]
43 exposed *dyncfg.ExposedCache[secretstore.Config]
44 service secretstore.Service
45 pluginName string
46 initial []secretstore.Config
47
48 affectedJobs func(string) []secretstore.JobRef
49 restartableAffectedJobs func(string) []secretstore.JobRef
50 restartDependentJobs func(string) string
51
52 handler *dyncfg.Handler[secretstore.Config]
53 cb *secretStoreCallbacks
54 }
55
56 func New(opts Options) *Controller {
57 seen := opts.Seen
58 if seen == nil {
59 seen = dyncfg.NewSeenCache[secretstore.Config]()
60 }
61 exposed := opts.Exposed
62 if exposed == nil {
63 exposed = dyncfg.NewExposedCache[secretstore.Config]()
64 }
65
66 c := &Controller{
67 Logger: opts.Logger,
68 api: opts.API,
69 seen: seen,
70 exposed: exposed,
71 service: opts.Service,
72 pluginName: opts.Plugin,
73 initial: append([]secretstore.Config(nil), opts.Initial...),
74 affectedJobs: opts.AffectedJobs,
75 restartableAffectedJobs: opts.RestartableAffectedJobs,
76 restartDependentJobs: opts.RestartDependentJobs,
77 }
78 c.cb = newSecretStoreCallbacks(secretStoreCallbackDeps{
79 pluginName: c.pluginName,
80 log: c.Logger,
81 service: c.service,
82 restartDependentJobs: c.restartDependentJobs,
83 })
84 c.handler = dyncfg.NewHandler(dyncfg.HandlerOpts[secretstore.Config]{
85 Logger: c.Logger,
86 API: c.api,
87 Seen: c.seen,
88 Exposed: c.exposed,
89 Callbacks: c.cb,
90 Path: fmt.Sprintf(dyncfgSecretStorePath, c.pluginName),
91 JobCommands: []dyncfg.Command{
92 dyncfg.CommandSchema,
93 dyncfg.CommandGet,
94 dyncfg.CommandUpdate,
95 dyncfg.CommandTest,
96 dyncfg.CommandUserconfig,
97 },
98 })
99 return c
100 }
101
102 func (c *Controller) Prefix() string {
103 return fmt.Sprintf(dyncfgSecretStorePrefixf, c.pluginName)
104 }
105
106 func (c *Controller) CreateTemplates() {
107 if c.service == nil || c.api == nil {
108 return
109 }
110 for _, kind := range c.service.Kinds() {
111 c.api.ConfigCreate(netdataapi.ConfigOpts{
112 ID: c.templateID(kind),
113 Status: dyncfg.StatusAccepted.String(),
114 ConfigType: dyncfg.ConfigTypeTemplate.String(),
115 Path: fmt.Sprintf(dyncfgSecretStorePath, c.pluginName),
116 SourceType: "internal",
117 Source: "internal",
118 SupportedCommands: dyncfgSecretStoreTemplateCmds(),
119 })
120 }
121 }
122
123 func (c *Controller) PublishExisting() {
124 if len(c.initial) != 0 {
125 for _, cfg := range c.initial {
126 c.publishInitialConfig(cfg)
127 }
128 c.initial = nil
129 return
130 }
131
132 c.exposed.ForEach(func(_ string, entry *dyncfg.Entry[secretstore.Config]) bool {
133 c.handler.NotifyJobCreate(entry.Cfg, entry.Status)
134 return true
135 })
136 }
137
138 func (c *Controller) SetAPI(api *dyncfg.Responder) {
139 if api == nil {
140 // Nil means "keep the current responder" rather than clearing output wiring.
141 return
142 }
143 c.api = api
144 if c.handler != nil {
145 c.handler.SetAPI(api)
146 }
147 }
148
149 func (c *Controller) Service() secretstore.Service {
150 return c.service
151 }
152
153 func (c *Controller) configID(id string) string {
154 return fmt.Sprintf("%s%s", c.Prefix(), id)
155 }
156
157 func (c *Controller) templateID(kind secretstore.StoreKind) string {
158 return fmt.Sprintf("%s%s", c.Prefix(), kind)
159 }
160
161 func entryFromDyncfg(entry *dyncfg.Entry[secretstore.Config]) Entry {
162 if entry == nil {
163 return Entry{}
164 }
165 return Entry{Cfg: entry.Cfg, Status: entry.Status}
166 }