1 package services
2
3 import (
4 "context"
5 "database/sql"
6 "fmt"
7 "slices"
8 "strings"
9
10 sqlerr "edge-infra.dev/pkg/edge/api/apierror/sql"
11 "edge-infra.dev/pkg/edge/api/graph/model"
12 sqlquery "edge-infra.dev/pkg/edge/api/sql"
13 "edge-infra.dev/pkg/edge/api/utils"
14 "edge-infra.dev/pkg/edge/capabilities"
15 "edge-infra.dev/pkg/edge/chariot/client"
16 "edge-infra.dev/pkg/lib/uuid"
17 v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
18 )
19
20
21 type TerminalLabelService interface {
22 CreateTerminalLabel(context.Context, string, ...string) error
23 GetTerminalLabel(context.Context, string) (*model.TerminalLabel, error)
24 DeleteTerminalLabels(context.Context, model.SearchTerminalLabelInput) ([]*model.TerminalLabel, error)
25 SendUpdatedIENCRAfterDeletion(context.Context, []*model.TerminalLabel) error
26 GetTerminalLabels(context.Context, model.SearchTerminalLabelInput) ([]*model.TerminalLabel, error)
27 GetTerminalLabelsInfo(ctx context.Context, terminals []*model.Terminal) ([]*model.Terminal, error)
28 }
29
30 type terminalLabelService struct {
31 SQLDB *sql.DB
32 ChariotService ChariotService
33 TerminalService TerminalService
34 StoreClusterService StoreClusterService
35 LabelService LabelService
36 }
37
38 func (n *terminalLabelService) CreateTerminalLabel(ctx context.Context, terminalID string, labelEdgeIDs ...string) error {
39 tx, err := n.SQLDB.BeginTx(ctx, &sql.TxOptions{})
40 if err != nil {
41 return err
42 }
43 customLabels, err := insertTerminalLabels(ctx, tx, terminalID, labelEdgeIDs...)
44 if err != nil {
45 if rollbackErr := tx.Rollback(); rollbackErr != nil {
46 return rollbackErr
47 }
48 return err
49 }
50 getLabel := false
51 terminal, err := n.TerminalService.GetTerminal(ctx, terminalID, &getLabel)
52 if err != nil {
53 if rollbackErr := tx.Rollback(); rollbackErr != nil {
54 return rollbackErr
55 }
56 return err
57 }
58 cluster, err := n.StoreClusterService.GetCluster(ctx, terminal.ClusterEdgeID)
59 if err != nil {
60 if rollbackErr := tx.Rollback(); rollbackErr != nil {
61 return rollbackErr
62 }
63 return err
64 }
65 clusterNetworkServices, err := n.StoreClusterService.GetClusterNetworkServices(ctx, cluster.ClusterEdgeID)
66 if err != nil {
67 if rollbackErr := tx.Rollback(); rollbackErr != nil {
68 return rollbackErr
69 }
70 return err
71 }
72 ienNodeCRBase64, err := n.TerminalService.CreateDSDSIENodeCR(terminal, clusterNetworkServices, customLabels, cluster.FleetVersion)
73 if err != nil {
74 if rollbackErr := tx.Rollback(); rollbackErr != nil {
75 return rollbackErr
76 }
77 return err
78 }
79
80 msg := client.NewChariotMessage().
81 SetBanner(cluster.ProjectID).
82 SetOperation(client.Create).
83 SetCluster(cluster.ClusterEdgeID).
84 SetOwner(ComponentOwner).
85 AddObject(ienNodeCRBase64)
86 if err := n.ChariotService.InvokeChariotPubsub(ctx, msg, make(map[string]string)); err != nil {
87 if rollbackErr := tx.Rollback(); rollbackErr != nil {
88 return rollbackErr
89 }
90 return err
91 }
92 return tx.Commit()
93 }
94
95 func (n *terminalLabelService) GetTerminalLabel(ctx context.Context, terminalID string) (*model.TerminalLabel, error) {
96 terminalLabel := &model.TerminalLabel{}
97 row := n.SQLDB.QueryRowContext(ctx, sqlquery.GetTerminalLabel, terminalID)
98 err := row.Scan(&terminalLabel.TerminalID, &terminalLabel.TerminalLabelEdgeID, &terminalLabel.LabelEdgeID)
99
100 if err != nil {
101 return nil, err
102 }
103 labelDetails, err := n.LabelService.GetLabel(ctx, terminalLabel.LabelEdgeID)
104
105 if err != nil {
106
107 terminalLabel.Label = nil
108 } else {
109 terminalLabel.Label = labelDetails
110 }
111
112 return terminalLabel, err
113 }
114
115 func (n *terminalLabelService) GetTerminalLabels(ctx context.Context, terminalLabelInput model.SearchTerminalLabelInput) ([]*model.TerminalLabel, error) {
116 terminalLabels := make([]*model.TerminalLabel, 0)
117 if terminalLabelInput.ClusterEdgeID != nil {
118 terminalrows, err := n.SQLDB.QueryContext(ctx, sqlquery.GetTerminalByClusterEdgeIDQuery, terminalLabelInput.ClusterEdgeID)
119 if err != nil {
120 return nil, err
121 }
122 terminals := []*model.Terminal{}
123 for terminalrows.Next() {
124 terminal := &model.Terminal{}
125 err := terminalrows.Scan(&terminal.TerminalID, &terminal.Lane, &terminal.Role, &terminal.ClusterEdgeID, &terminal.ClusterName, &terminal.Class, &terminal.DiscoverDisks, &terminal.BootDisk, &terminal.PrimaryInterface, &terminal.ExistingEfiPart, &terminal.SwapEnabled, &terminal.Hostname)
126 if err != nil {
127 return nil, err
128 }
129 terminals = append(terminals, terminal)
130 }
131 if err := terminalrows.Err(); err != nil {
132 return nil, sqlerr.Wrap(err)
133 }
134 for _, terminal := range terminals {
135 terminalID := terminal.TerminalID
136 if terminalLabelInput.TerminalID != nil {
137 terminalID = *terminalLabelInput.TerminalID
138 }
139 terminalLabelsrow, err := n.SQLDB.QueryContext(ctx, sqlquery.GetTerminalLabels, terminalID, terminalLabelInput.LabelEdgeID)
140 if err != nil {
141 return nil, err
142 }
143 for terminalLabelsrow.Next() {
144 terminalLabel := &model.TerminalLabel{Label: &model.Label{}}
145 err := terminalLabelsrow.Scan(&terminalLabel.TerminalID, &terminalLabel.TerminalLabelEdgeID, &terminalLabel.LabelEdgeID,
146 &terminalLabel.Label.Key, &terminalLabel.Label.Color, &terminalLabel.Label.Visible, &terminalLabel.Label.Editable,
147 &terminalLabel.Label.BannerEdgeID, &terminalLabel.Label.Unique, &terminalLabel.Label.Description, &terminalLabel.Label.Type)
148 if err != nil {
149 return nil, err
150 }
151 terminalLabels = append(terminalLabels, terminalLabel)
152 }
153 if err := terminalLabelsrow.Err(); err != nil {
154 return nil, sqlerr.Wrap(err)
155 }
156 }
157 return terminalLabels, nil
158 }
159 rows, err := n.SQLDB.QueryContext(ctx, sqlquery.GetTerminalLabels, terminalLabelInput.TerminalID, terminalLabelInput.LabelEdgeID)
160 if err != nil {
161 return terminalLabels, err
162 }
163 for rows.Next() {
164 terminalLabel := &model.TerminalLabel{Label: &model.Label{}}
165
166 err := rows.Scan(&terminalLabel.TerminalID, &terminalLabel.TerminalLabelEdgeID, &terminalLabel.LabelEdgeID,
167 &terminalLabel.Label.Key, &terminalLabel.Label.Color, &terminalLabel.Label.Visible, &terminalLabel.Label.Editable,
168 &terminalLabel.Label.BannerEdgeID, &terminalLabel.Label.Unique, &terminalLabel.Label.Description, &terminalLabel.Label.Type)
169 if err != nil {
170 return terminalLabels, err
171 }
172 terminalLabels = append(terminalLabels, terminalLabel)
173 }
174 if err := rows.Err(); err != nil {
175 return nil, sqlerr.Wrap(err)
176 }
177 return terminalLabels, nil
178 }
179
180 func (n *terminalLabelService) GetTerminalLabelsInfo(ctx context.Context, terminals []*model.Terminal) ([]*model.Terminal, error) {
181 for _, terminal := range terminals {
182 terminalLabels, err := n.GetTerminalLabels(ctx, model.SearchTerminalLabelInput{TerminalID: &terminal.TerminalID})
183 if err != nil {
184 return terminals, err
185 }
186 terminal.Labels = terminalLabels
187 }
188 return terminals, nil
189 }
190
191 func insertTerminalLabels(ctx context.Context, tx *sql.Tx, terminalID string, labelEdgeIDs ...string) (map[string]string, error) {
192 customLabels := make(map[string]string, 0)
193 rows, err := tx.QueryContext(ctx, sqlquery.GetTerminalLabels, terminalID, sql.NullString{})
194
195 if err != nil && !strings.Contains(err.Error(), "no rows in result set") {
196 return customLabels, err
197 }
198 for rows.Next() {
199 terminalLabel := &model.TerminalLabel{Label: &model.Label{}}
200
201 err := rows.Scan(&terminalLabel.TerminalID, &terminalLabel.TerminalLabelEdgeID, &terminalLabel.LabelEdgeID,
202 &terminalLabel.Label.Key, &terminalLabel.Label.Color, &terminalLabel.Label.Visible, &terminalLabel.Label.Editable,
203 &terminalLabel.Label.BannerEdgeID, &terminalLabel.Label.Unique, &terminalLabel.Label.Description, &terminalLabel.Label.Type)
204 if err != nil {
205 return customLabels, err
206 }
207
208 labelDescription := uuid.FromUUID(terminalLabel.Label.Description).Hash()
209 labelKey := utils.ToK8sName(terminalLabel.Label.Key)
210 if err = utils.ValuesValidation([]string{labelDescription, labelKey}); err != nil {
211 return customLabels, err
212 }
213
214
215 key := formatCustomLabelKey(terminalLabel.Label)
216 customLabels[key] = labelDescription
217 if terminalLabel.Label.Type != "" && !slices.Contains(capabilities.EdgeAutomatedCapabilityLabelTypes, terminalLabel.Label.Type) {
218 customLabels[fmt.Sprintf(v1ien.CustomNodeLabel, terminalLabel.Label.Type)] = labelKey
219 }
220 }
221 if err := rows.Err(); err != nil {
222 return nil, sqlerr.Wrap(err)
223 }
224 for _, labelEdgeID := range labelEdgeIDs {
225 _, err := tx.ExecContext(ctx, sqlquery.InsertTerminalLabel, terminalID, labelEdgeID)
226 if err != nil {
227 return customLabels, err
228 }
229 label := &model.Label{}
230 row := tx.QueryRowContext(ctx, sqlquery.GetLabelQuery, labelEdgeID)
231 if err := row.Scan(&label.LabelEdgeID, &label.Key, &label.Color, &label.Visible, &label.Editable, &label.BannerEdgeID, &label.Unique, &label.Description, &label.Type); err != nil {
232 return customLabels, err
233 }
234
235 labelDescription := uuid.FromUUID(label.Description).Hash()
236 labelKey := utils.ToK8sName(label.Key)
237 if err = utils.ValuesValidation([]string{labelDescription, labelKey}); err != nil {
238 return customLabels, err
239 }
240 key := formatCustomLabelKey(label)
241 customLabels[key] = labelDescription
242 if label.Type != "" && !slices.Contains(capabilities.EdgeAutomatedCapabilityLabelTypes, label.Type) {
243 customLabels[fmt.Sprintf(v1ien.CustomNodeLabel, label.Type)] = labelKey
244 }
245 }
246 return customLabels, nil
247 }
248
249 func (n *terminalLabelService) DeleteTerminalLabels(ctx context.Context, terminalLabelInput model.SearchTerminalLabelInput) ([]*model.TerminalLabel, error) {
250 terminaLabels, err := n.GetTerminalLabels(ctx, terminalLabelInput)
251 if err != nil {
252 return nil, err
253 }
254 for _, terminaLabel := range terminaLabels {
255 _, err = n.SQLDB.ExecContext(ctx, sqlquery.DeleteTerminalLabel, terminaLabel.TerminalLabelEdgeID)
256 if err != nil {
257 return nil, err
258 }
259 }
260 return terminaLabels, nil
261 }
262
263 func (n *terminalLabelService) SendUpdatedIENCRAfterDeletion(ctx context.Context, affectedTerminalLabels []*model.TerminalLabel) error {
264
265 updatedTerminals := make(map[string]map[string]string, 1)
266 for _, terminalLabel := range affectedTerminalLabels {
267 updatedTerminals[terminalLabel.TerminalID] = make(map[string]string)
268 }
269 getLabels := true
270 for terminalID, customLabels := range updatedTerminals {
271 terminal, err := n.TerminalService.GetTerminal(ctx, terminalID, &getLabels)
272 if err != nil {
273 return err
274 }
275
276 for _, terminalLabel := range terminal.Labels {
277 key := formatCustomLabelKey(terminalLabel.Label)
278 customLabels[key] = uuid.FromUUID(terminalLabel.Label.Description).Hash()
279 if terminalLabel.Label.Type != "" && !slices.Contains(capabilities.EdgeAutomatedCapabilityLabelTypes, terminalLabel.Label.Type) {
280 customLabels[fmt.Sprintf(v1ien.CustomNodeLabel, terminalLabel.Label.Key)] = utils.ToK8sName(terminalLabel.Label.Description)
281 }
282 }
283 cluster, err := n.StoreClusterService.GetCluster(ctx, terminal.ClusterEdgeID)
284 if err != nil {
285 return err
286 }
287 clusterNetworkServices, err := n.StoreClusterService.GetClusterNetworkServices(ctx, cluster.ClusterEdgeID)
288 if err != nil {
289 return err
290 }
291 ienNodeCRBase64, err := n.TerminalService.CreateDSDSIENodeCR(terminal, clusterNetworkServices, customLabels, cluster.FleetVersion)
292 if err != nil {
293 return err
294 }
295 msg := client.NewChariotMessage().
296 SetBanner(cluster.ProjectID).
297 SetOperation(client.Create).
298 SetCluster(cluster.ClusterEdgeID).
299 SetOwner(ComponentOwner).
300 AddObject(ienNodeCRBase64)
301 if err := n.ChariotService.InvokeChariotPubsub(ctx, msg, make(map[string]string)); err != nil {
302 return err
303 }
304 }
305 return nil
306 }
307
308 func NewTerminalLabelService(sqlDB *sql.DB, chariotSvc ChariotService, terminalSvc TerminalService, storeClusterSvc StoreClusterService, labelSvc LabelService) TerminalLabelService {
309 return &terminalLabelService{
310 SQLDB: sqlDB,
311 ChariotService: chariotSvc,
312 TerminalService: terminalSvc,
313 StoreClusterService: storeClusterSvc,
314 LabelService: labelSvc,
315 }
316 }
317
318
319 func formatCustomLabelKey(label *model.Label) string {
320 if !slices.Contains(capabilities.EdgeAutomatedCapabilityLabelTypes, label.Type) {
321 return fmt.Sprintf(v1ien.CustomNodeLabel, label.Key)
322 }
323 return label.Key
324 }
325
View as plain text