1 package remoteagentconfig
2
3 import (
4 "bytes"
5 "context"
6 "errors"
7 "fmt"
8 "io/fs"
9 "sort"
10 "strconv"
11 "strings"
12 "text/template"
13 "time"
14
15 corev1 "k8s.io/api/core/v1"
16 ctrl "sigs.k8s.io/controller-runtime"
17 "sigs.k8s.io/controller-runtime/pkg/client"
18
19 "edge-infra.dev/pkg/edge/info"
20 "edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/config"
21 "edge-infra.dev/pkg/sds/lib/os/file"
22 )
23
24 var (
25 remoteAgentHostDataDir = "/host-data/remote-access-agent"
26 remoteAgentSecretDir = remoteAgentHostDataDir
27 remoteAgentDataDir = "/data/remote-access-agent"
28 remoteAgentConfigFileName = "config.yaml"
29 remoteAgentSFileName = "%d.adc.json"
30 configFileMode = fs.FileMode(0644)
31 secretFileMode = fs.FileMode(0644)
32
33 secretNamespace = "sds"
34 secretName = "remote-agent-configuration"
35 configEntry = "config.yaml.tpl"
36 adcEntry = "key.json"
37 )
38
39 type Plugin struct{}
40
41 type templateFields struct {
42 TerminalID string
43 StoreID string
44 BannerID string
45 Provider string
46 CredentialsPath string
47 }
48
49 func (remoteAccessAgentPlugin Plugin) Reconcile(ctx context.Context, object client.Object, conf config.Config) error {
50 secret, ok := object.(*corev1.Secret)
51 if !ok {
52 return nil
53 }
54
55 edgeInfo, err := conf.GetEdgeInfoConfig(ctx)
56 if err != nil {
57 return err
58 }
59
60 log := ctrl.LoggerFrom(ctx, "pluginName", "remoteagentconfig")
61 ctrl.LoggerInto(ctx, log)
62
63
64 if !isTargetSecret(secret) {
65 return nil
66 }
67
68 terminalInfo, err := getTerminalInfo(ctx, conf)
69 if err != nil {
70 return err
71 }
72
73
74 fileHandler := file.New()
75 return UpdateRemoteAgentConfig(ctx, secret, terminalInfo, edgeInfo, fileHandler)
76 }
77
78 func getTerminalInfo(ctx context.Context, conf config.Config) (map[string]string, error) {
79 ienode, err := conf.GetHostIENode(ctx)
80 if err != nil {
81 return nil, fmt.Errorf("locating IENode: %w", err)
82 }
83
84 return map[string]string{"terminalID": ienode.ObjectMeta.Labels["node.ncr.com/terminal-id"]}, nil
85 }
86
87 func isTargetSecret(secret *corev1.Secret) bool {
88 return secret.Name == secretName && secret.Namespace == secretNamespace
89 }
90
91 func UpdateRemoteAgentConfig(ctx context.Context, secret *corev1.Secret, terminalInfo map[string]string, edgeConf *info.EdgeInfo, fileHandler file.File) error {
92 newSecret, err := getSecretData(secret)
93 if err != nil {
94 return err
95 }
96
97 adcFiles, err := updateADCJSON(ctx, newSecret, fileHandler)
98 if err != nil {
99 return err
100 }
101
102 recentADCidx := len(adcFiles) - 1
103 latestADC := adcFiles[recentADCidx]
104
105 err = updateConfigyaml(ctx, secret, latestADC, terminalInfo, edgeConf, fileHandler)
106 if err != nil {
107 return err
108 }
109
110
111 for _, file := range adcFiles[:recentADCidx] {
112 err = fileHandler.Remove(remoteAgentSecretDir + "/" + file)
113 if err != nil {
114 return err
115 }
116 }
117
118 return nil
119 }
120
121 func updateConfigyaml(ctx context.Context, secret *corev1.Secret, adcFilepath string, terminalInfo map[string]string, edgeConf *info.EdgeInfo, fileHandler file.File) error {
122 conf, err := templateConfig(secret, adcFilepath, terminalInfo, edgeConf)
123 if err != nil {
124 return err
125 }
126
127 currentData, err := fileHandler.Read(remoteAgentHostDataDir + "/" + remoteAgentConfigFileName)
128 if err != nil {
129 if !errors.Is(err, fs.ErrNotExist) {
130 return err
131 }
132 currentData = []byte{}
133 }
134
135 if bytes.Equal(conf, currentData) {
136 return nil
137 }
138
139 ctrl.LoggerFrom(ctx).Info("Updating config.yaml data")
140 return fileHandler.SafeWrite(remoteAgentHostDataDir, remoteAgentConfigFileName, conf, configFileMode)
141 }
142
143
144 func getSecretData(secret *corev1.Secret) ([]byte, error) {
145 data, ok := secret.Data[adcEntry]
146 if !ok || data == nil {
147 return nil, fmt.Errorf("cannot find adc entry %s in secret %s", adcEntry, secret.Name)
148 }
149
150 return data, nil
151 }
152
153
154 func sortedADCFiles(fileHandler file.File) ([]string, error) {
155 allFiles, err := fileHandler.ReadDir(remoteAgentSecretDir)
156 if err != nil {
157 return nil, fmt.Errorf("listing adc entries: %w", err)
158 }
159
160
161 var adcFiles []struct {
162 timestamp int64
163 filename string
164 }
165
166 for _, file := range allFiles {
167 if !strings.HasSuffix(file.Name(), ".adc.json") {
168 continue
169 }
170
171 parts := strings.Split(file.Name(), ".")
172 if len(parts) != 3 {
173 continue
174 }
175
176 timestamp, err := strconv.ParseInt(parts[0], 10, 64)
177 if err != nil {
178 continue
179 }
180
181 adcFiles = append(adcFiles, struct {
182 timestamp int64
183 filename string
184 }{
185 timestamp: timestamp,
186 filename: file.Name(),
187 })
188 }
189
190
191 sort.Slice(adcFiles, func(i, j int) bool { return adcFiles[i].timestamp < adcFiles[j].timestamp })
192
193 var adcFilenames []string
194 for _, file := range adcFiles {
195 adcFilenames = append(adcFilenames, file.filename)
196 }
197
198 return adcFilenames, nil
199 }
200
201
202
203 func getCurrentADCData(adcFilenames []string, fileHandler file.File) ([]byte, error) {
204 recentIdx := len(adcFilenames) - 1
205
206 if recentIdx < 0 {
207 return []byte{}, nil
208 }
209
210 currentData, err := fileHandler.Read(remoteAgentSecretDir + "/" + adcFilenames[recentIdx])
211 if err != nil {
212 if !errors.Is(err, fs.ErrNotExist) {
213 return nil, fmt.Errorf("reading adc file: %w", err)
214 }
215 currentData = []byte{}
216 }
217
218 return currentData, nil
219 }
220
221
222
223
224
225 func updateADCJSON(ctx context.Context, secretData []byte, fileHandler file.File) ([]string, error) {
226 adcFilenames, err := sortedADCFiles(fileHandler)
227 if err != nil {
228 return nil, err
229 }
230
231 currentData, err := getCurrentADCData(adcFilenames, fileHandler)
232 if err != nil {
233 return nil, err
234 }
235
236 if bytes.Equal(secretData, currentData) {
237 return adcFilenames, nil
238 }
239
240 newFilename := fmt.Sprintf(remoteAgentSFileName, time.Now().Unix())
241
242 ctrl.LoggerFrom(ctx).Info("Updating adc secret", "adcFile", newFilename)
243 err = fileHandler.SafeWrite(remoteAgentSecretDir, newFilename, secretData, secretFileMode)
244 if err != nil {
245 return nil, fmt.Errorf("writing adc file: %w", err)
246 }
247
248 return append(adcFilenames, newFilename), nil
249 }
250
251 func templateConfig(secret *corev1.Secret, adcFilepath string, terminalInfo map[string]string, edgeConf *info.EdgeInfo) ([]byte, error) {
252 var res bytes.Buffer
253 values := templateFields{
254 TerminalID: terminalInfo["terminalID"],
255 StoreID: edgeConf.ClusterEdgeID,
256 BannerID: edgeConf.BannerEdgeID,
257 Provider: edgeConf.ProjectID,
258 CredentialsPath: remoteAgentDataDir + "/" + adcFilepath,
259 }
260
261 data, ok := secret.Data[configEntry]
262 if !ok || data == nil {
263 return nil, fmt.Errorf("cannot find template %s in secret", configEntry)
264 }
265
266 template, err := template.New(remoteAgentConfigFileName + ".tpl").Parse(string(data))
267 if err != nil {
268 return nil, err
269 }
270
271 err = template.Execute(&res, values)
272 if err != nil {
273 return nil, err
274 }
275
276 return res.Bytes(), nil
277 }
278
View as plain text