1 package services
2
3 import (
4 "context"
5 "database/sql"
6 "encoding/hex"
7 "encoding/json"
8 "errors"
9 "fmt"
10 "slices"
11 "strings"
12 "time"
13
14 "github.com/google/uuid"
15 "gopkg.in/yaml.v2"
16
17 sqlerr "edge-infra.dev/pkg/edge/api/apierror/sql"
18 "edge-infra.dev/pkg/edge/api/clients"
19 "edge-infra.dev/pkg/edge/api/graph/mapper"
20 "edge-infra.dev/pkg/edge/api/graph/model"
21 edgenode "edge-infra.dev/pkg/edge/api/services/edgenode/common"
22 sqlquery "edge-infra.dev/pkg/edge/api/sql"
23 "edge-infra.dev/pkg/edge/api/status"
24 "edge-infra.dev/pkg/edge/api/utils"
25 "edge-infra.dev/pkg/lib/crypto"
26 "edge-infra.dev/pkg/lib/runtime/version"
27 cc "edge-infra.dev/pkg/sds/clustersecrets/common"
28 dsv1 "edge-infra.dev/pkg/sds/devices/k8s/apis/v1"
29 v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
30 )
31
32
33 type TerminalService interface {
34 CreateTerminalEntry(ctx context.Context, newTerminal *model.TerminalCreateInput, activationCode crypto.Credential) (*model.Terminal, error)
35 UpdateTerminalEntry(ctx context.Context, terminal *model.TerminalIDInput) (*model.Terminal, error)
36 DeleteTerminalEntry(ctx context.Context, terminalID string) error
37 CreateTerminalInterfaceEntry(ctx context.Context, terminalID string, newTerminalInterface *model.TerminalInterfaceCreateInput) (*model.TerminalInterface, error)
38 UpdateTerminalInterfaceEntry(ctx context.Context, updatedInterface *model.TerminalInterfaceIDInput) (*model.TerminalInterface, error)
39 DeleteTerminalInterfaceEntry(ctx context.Context, terminalInterfaceID string) (*model.Terminal, error)
40 CreateTerminalAddressEntry(ctx context.Context, terminalInterfaceID string, newTerminalAddress *model.TerminalAddressCreateInput) (*model.TerminalAddress, error)
41 UpdateTerminalAddressEntry(ctx context.Context, updatedAddress *model.TerminalAddressIDInput) (*model.TerminalAddress, error)
42 DeleteTerminalAddressEntry(ctx context.Context, terminalAddressID string) (*model.Terminal, error)
43 GetTerminal(ctx context.Context, terminalID string, getLabels *bool) (*model.Terminal, error)
44 GetTerminals(ctx context.Context, clusterEdgeID *string, terminalHostname *string) ([]*model.Terminal, error)
45 GetBannerTerminals(ctx context.Context, bannerEdgeID, projectID string) ([]*model.Terminal, error)
46 CreateDSDSIENodeCR(terminal *model.Terminal, clusterNetworkServices []*model.ClusterNetworkServiceInfo, customLabels map[string]string, edgeVersion string) (string, error)
47 CreateCICIENodeCR(terminal *model.Terminal, clusterNetworkServices []*model.ClusterNetworkServiceInfo, customLabels map[string]string, edgeVersion string) (string, error)
48 GetTerminalFromInterface(ctx context.Context, terminalInterfaceID string) (*model.Terminal, error)
49 GetTerminalFromAddress(ctx context.Context, terminalAddressID string) (*model.Terminal, error)
50 GetTerminalBootstrapConfig(ctx context.Context, terminal *model.Terminal, clusterNetworkServices []*model.ClusterNetworkServiceInfo, breakGlassSecret, grubSecret cc.Secret, bootstrapAck, isFirstNode bool, organization string, bootstrapTokenValues []*model.KeyValues, clusterCaHash, endpoint string) (string, error)
51 RemoveClusterEdgeBootstrapToken(ctx context.Context, clusterEdgeID string) error
52 CreateTerminalDiskEntry(ctx context.Context, terminalID string, newTerminalDisk *model.TerminalDiskCreateInput) (*model.TerminalDisk, error)
53 DeleteTerminalDiskEntry(ctx context.Context, terminalDiskID string) (*model.Terminal, error)
54 UpdateTerminalDiskEntry(ctx context.Context, terminalDiskID string, updatedDisk model.TerminalDiskUpdateInput) (*model.TerminalDisk, error)
55 GetTerminalFromDisk(ctx context.Context, terminalDiskID string) (*model.Terminal, error)
56 GetTerminalNodeStatus(ctx context.Context, clusterEdgeID, terminalID, hostname string) (*model.TerminalStatus, error)
57 GetNodeReplicationStatus(ctx context.Context, clusterEdgeID, hostname string) (*model.ReplicationStatus, error)
58 GetTerminalsByClusterID(ctx context.Context, clusterEdgeID string) ([]*model.Terminal, error)
59 TerminalDevices(ctx context.Context, terminalID string) (*model.TerminalDevices, error)
60 }
61
62 type terminalService struct {
63 SQLDB *sql.DB
64 BQClient clients.BQClient
65 LabelService LabelService
66 StoreClusterService StoreClusterService
67 }
68
69 var (
70 getLabel = true
71 ErrDuplicateTerminalDiskDevicePaths = errors.New("terminals cannot have disks with duplicate device paths")
72 )
73
74 func (t *terminalService) CreateTerminalEntry(ctx context.Context, newTerminal *model.TerminalCreateInput, activationCode crypto.Credential) (*model.Terminal, error) {
75 if err := utils.ValidateTerminalCreateInput(newTerminal); err != nil {
76 return nil, err
77 }
78
79 transaction, err := t.SQLDB.BeginTx(ctx, nil)
80 if err != nil {
81 return nil, err
82 }
83 defer transaction.Rollback()
84
85 row := transaction.QueryRowContext(ctx, sqlquery.GetClusterNameByClusterEdgeIDQuery, newTerminal.ClusterEdgeID)
86 var clusterName string
87 err = row.Scan(&clusterName)
88 if err != nil {
89 return nil, err
90 }
91
92
93 if len(newTerminal.Interfaces) == 0 {
94 return nil, errors.New("unable to create terminal with no interfaces")
95 }
96
97 var hostname string
98 if newTerminal.Hostname == nil {
99 hostname = utils.CreateIENodeHostname(newTerminal.Interfaces[0].MacAddress)
100 } else {
101 hostname = strings.ToLower(*newTerminal.Hostname)
102 }
103 newTerminal.Hostname = &hostname
104
105
106 if err := t.checkHostnames(ctx, hostname, newTerminal); err != nil {
107 return nil, err
108 }
109
110
111 if newTerminal.Lane != nil && *newTerminal.Lane != "" {
112 if err := t.checkLaneDuplicates(ctx, *newTerminal.Lane, newTerminal.ClusterEdgeID); err != nil {
113 return nil, err
114 }
115 }
116
117 terminal := utils.CreateTerminalModel(uuid.NewString(), newTerminal.ClusterEdgeID, newTerminal.Lane, newTerminal.Role, newTerminal.Class, newTerminal.DiscoverDisks, newTerminal.BootDisk, clusterName, *newTerminal.Hostname, newTerminal.ExistingEfiPart, *newTerminal.SwapEnabled)
118
119
120 if err = utils.ValidateTerminal(&terminal); err != nil {
121 return nil, err
122 }
123
124 hash := activationCode.Hashed()
125 hashEncoded := hex.EncodeToString(hash)
126
127 args := []interface{}{
128 terminal.TerminalID,
129 terminal.Lane,
130 terminal.Role,
131 terminal.Class,
132 terminal.DiscoverDisks,
133 terminal.BootDisk,
134 terminal.ClusterEdgeID,
135 terminal.Hostname,
136 hashEncoded,
137 terminal.ExistingEfiPart,
138 terminal.SwapEnabled,
139 }
140
141 if _, err = transaction.ExecContext(ctx, sqlquery.TerminalCreateQuery, args...); err != nil {
142 return nil, err
143 }
144
145
146 if hasDuplicateDevicePathsCreate(newTerminal.Disks) {
147 return nil, ErrDuplicateTerminalDiskDevicePaths
148 }
149
150 terminalDisk, err := t.createTerminalDiskEntries(ctx, transaction, newTerminal.Disks, terminal.TerminalID)
151 if err != nil {
152 return nil, err
153 }
154 terminal.Disks = terminalDisk
155
156
157 terminal.Interfaces, err = t.createTerminalInterfaceEntries(ctx, transaction, newTerminal.Interfaces, terminal.TerminalID)
158 if err != nil {
159 if rollbackErr := transaction.Rollback(); rollbackErr != nil {
160 return nil, rollbackErr
161 }
162 return nil, err
163 }
164
165 if err = transaction.Commit(); err != nil {
166 return nil, err
167 }
168
169 return &terminal, nil
170 }
171
172
173 func hasDuplicateDevicePathsCreate(diskList []*model.TerminalDiskCreateInput) bool {
174 diskPaths := []string{}
175 for _, disk := range diskList {
176 if slices.Contains(diskPaths, disk.DevicePath) {
177 return true
178 }
179 diskPaths = append(diskPaths, disk.DevicePath)
180 }
181 return false
182 }
183
184 func (t *terminalService) createTerminalInterfaceEntries(ctx context.Context, transaction *sql.Tx, newInterfaces []*model.TerminalInterfaceCreateInput, terminalID string) ([]*model.TerminalInterface, error) {
185 terminalInterfaces := []*model.TerminalInterface{}
186 for _, newInterface := range newInterfaces {
187 terminalInterface := utils.CreateTerminalIfaceModel(uuid.NewString(), strings.ToLower(newInterface.MacAddress), newInterface.Dhcp4, newInterface.Dhcp6, newInterface.Gateway4, newInterface.Gateway6, terminalID)
188
189 args := []interface{}{
190 terminalInterface.TerminalInterfaceID,
191 terminalInterface.MacAddress,
192 terminalInterface.Dhcp4,
193 terminalInterface.Dhcp6,
194 terminalInterface.Gateway4,
195 terminalInterface.Gateway6,
196 terminalID,
197 }
198
199 if _, err := transaction.ExecContext(ctx, sqlquery.TerminalInterfaceCreateQuery, args...); err != nil {
200 return nil, err
201 }
202
203
204 var err error
205 terminalInterface.Addresses, err = t.createTerminalAddressEntries(ctx, transaction, newInterface.Addresses, &terminalInterface)
206 if err != nil {
207 return nil, err
208 }
209 if err := utils.ValidateAllTerminalAddresses(terminalInterface.Dhcp4, terminalInterface.Addresses); err != nil {
210 return nil, err
211 }
212 terminalInterfaces = append(terminalInterfaces, &terminalInterface)
213 }
214 return terminalInterfaces, nil
215 }
216
217 func (t *terminalService) createTerminalAddressEntries(ctx context.Context, transaction *sql.Tx, newAddresses []*model.TerminalAddressCreateInput, iface *model.TerminalInterface) ([]*model.TerminalAddress, error) {
218 terminalAddresses := []*model.TerminalAddress{}
219 for _, newAddress := range newAddresses {
220 terminalAddress := utils.CreateTerminalAddressModel(uuid.NewString(), newAddress.IP, newAddress.PrefixLen, newAddress.Family, iface.TerminalInterfaceID)
221
222 if err := utils.ValidateTerminalAddress(&terminalAddress); err != nil {
223 return nil, err
224 }
225 args := []interface{}{
226 terminalAddress.TerminalAddressID,
227 terminalAddress.IP,
228 terminalAddress.PrefixLen,
229 terminalAddress.Family,
230 iface.TerminalInterfaceID,
231 }
232
233 if _, err := transaction.ExecContext(ctx, sqlquery.TerminalAddressCreateQuery, args...); err != nil {
234 return nil, err
235 }
236 terminalAddresses = append(terminalAddresses, &terminalAddress)
237 }
238 return terminalAddresses, nil
239 }
240
241 func (t *terminalService) UpdateTerminalEntry(ctx context.Context, updateTerminal *model.TerminalIDInput) (*model.Terminal, error) {
242 if err := utils.ValidateTerminalUpdateInput(updateTerminal.TerminalValues); err != nil {
243 return nil, err
244 }
245
246 transaction, err := t.SQLDB.BeginTx(ctx, nil)
247 if err != nil {
248 return nil, err
249 }
250 defer transaction.Rollback()
251
252
253 currentTerminal, err := t.GetTerminal(ctx, updateTerminal.TerminalID, &getLabel)
254 if err != nil {
255 return nil, err
256 }
257
258
259 if updateTerminal.TerminalValues.Lane != nil && *updateTerminal.TerminalValues.Lane != "" {
260 if err := t.checkLaneDuplicates(ctx, *updateTerminal.TerminalValues.Lane, currentTerminal.ClusterEdgeID); err != nil {
261 return nil, err
262 }
263 }
264
265
266 if updateTerminal.TerminalValues.PrimaryInterface != nil {
267 ac, err := edgenode.FetchActivationCode(ctx, t.SQLDB, updateTerminal.TerminalID)
268 if err != nil {
269 return nil, err
270 }
271 if ac == nil || len(*ac) == 0 {
272 return nil, errors.New("cannot change primary interface, terminal already bootstrapped")
273 }
274 }
275
276
277 currentTerminal = utils.UpdateTerminal(currentTerminal, updateTerminal)
278
279 if err = utils.ValidateTerminal(currentTerminal); err != nil {
280 return nil, err
281 }
282
283 args := []interface{}{
284 currentTerminal.Lane,
285 currentTerminal.Role,
286 currentTerminal.Class,
287 currentTerminal.DiscoverDisks,
288 currentTerminal.BootDisk,
289 currentTerminal.PrimaryInterface,
290 currentTerminal.ExistingEfiPart,
291 currentTerminal.SwapEnabled,
292 currentTerminal.TerminalID,
293 }
294
295
296 if _, err = transaction.ExecContext(ctx, sqlquery.TerminalUpdateQuery, args...); err != nil {
297 return nil, err
298 }
299
300
301 if hasDuplicateDevicePathsUpdate(updateTerminal.TerminalValues.Disks) {
302 return nil, ErrDuplicateTerminalDiskDevicePaths
303 }
304 terminalDisks, err := t.updateTerminalDiskEntries(ctx, transaction, updateTerminal.TerminalValues.Disks)
305 if err != nil {
306 if strings.Contains(err.Error(), "duplicate key value violates unique constraint \"unique_terminal_id_device_path\"") {
307 return nil, ErrDuplicateTerminalDiskDevicePaths
308 }
309 return nil, err
310 }
311 currentTerminal.Disks = terminalDisks
312
313
314 if err := t.updateTerminalInterfaceEntries(ctx, transaction, updateTerminal.TerminalValues.Interfaces, currentTerminal.Interfaces); err != nil {
315 if rollbackErr := transaction.Rollback(); rollbackErr != nil {
316 return nil, rollbackErr
317 }
318 return nil, err
319 }
320
321 if err = transaction.Commit(); err != nil {
322 return nil, err
323 }
324
325 currentTerminal, err = t.GetTerminal(ctx, updateTerminal.TerminalID, &getLabel)
326 if err != nil {
327 return nil, err
328 }
329 return currentTerminal, nil
330 }
331
332
333
334 func hasDuplicateDevicePathsUpdate(diskList []*model.TerminalDiskIDInput) bool {
335 diskPaths := []string{}
336 for _, disk := range diskList {
337 if disk == nil || disk.TerminalDiskValues == nil || disk.TerminalDiskValues.DevicePath == nil {
338 continue
339 }
340 diskPath := *disk.TerminalDiskValues.DevicePath
341 if slices.Contains(diskPaths, diskPath) {
342 return true
343 }
344 diskPaths = append(diskPaths, diskPath)
345 }
346 return false
347 }
348
349 func (t *terminalService) UpdateTerminalInterfaceEntry(ctx context.Context, updatedInterface *model.TerminalInterfaceIDInput) (*model.TerminalInterface, error) {
350 if err := utils.ValidateTerminalInterfaceUpdateInput(updatedInterface.TerminalInterfaceValues); err != nil {
351 return nil, err
352 }
353
354 updateIfaces := []*model.TerminalInterfaceIDInput{updatedInterface}
355 currentIface, err := t.getTerminalInterface(ctx, updatedInterface.TerminalInterfaceID)
356 if err != nil {
357 return nil, err
358 }
359 currentIfaces := []*model.TerminalInterface{currentIface}
360
361 transaction, err := t.SQLDB.BeginTx(ctx, nil)
362 if err != nil {
363 return nil, err
364 }
365
366 if err := t.updateTerminalInterfaceEntries(ctx, transaction, updateIfaces, currentIfaces); err != nil {
367 if rollbackErr := transaction.Rollback(); rollbackErr != nil {
368 return nil, rollbackErr
369 }
370 return nil, err
371 }
372
373 if err := transaction.Commit(); err != nil {
374 return nil, err
375 }
376
377 iface, err := t.getTerminalInterface(ctx, updatedInterface.TerminalInterfaceID)
378 if err != nil {
379 return nil, err
380 }
381 return iface, nil
382 }
383
384 func (t *terminalService) updateTerminalInterfaceEntries(ctx context.Context, transaction *sql.Tx, updateInterfaces []*model.TerminalInterfaceIDInput, currentInterfaces []*model.TerminalInterface) error {
385 for _, updateInterface := range updateInterfaces {
386 interfaceValues := updateInterface.TerminalInterfaceValues
387
388
389 currentInterface, err := utils.UpdateTerminalInterface(currentInterfaces, updateInterface)
390 if err != nil {
391 return err
392 }
393
394 args := []interface{}{
395 currentInterface.MacAddress,
396 currentInterface.Dhcp4,
397 currentInterface.Dhcp6,
398 currentInterface.Gateway4,
399 currentInterface.Gateway6,
400 currentInterface.TerminalInterfaceID,
401 }
402
403
404 if _, err = transaction.ExecContext(ctx, sqlquery.TerminalInterfaceUpdateQuery, args...); err != nil {
405 return err
406 }
407
408
409 if len(interfaceValues.Addresses) > 0 {
410 if err := t.updateTerminalAddressEntries(ctx, transaction, interfaceValues.Addresses, currentInterface.Addresses); err != nil {
411 return err
412 }
413 }
414
415
416 if err := utils.ValidateAllTerminalAddresses(currentInterface.Dhcp4, currentInterface.Addresses); err != nil {
417 return err
418 }
419 }
420 return nil
421 }
422
423 func (t *terminalService) updateTerminalAddressEntries(ctx context.Context, transaction *sql.Tx, updateAddresses []*model.TerminalAddressIDInput, currentAddresses []*model.TerminalAddress) error {
424 for _, updateAddress := range updateAddresses {
425
426 currentAddress, err := utils.UpdateTerminalAddress(currentAddresses, updateAddress)
427 if err != nil {
428 return err
429 }
430
431 if err = utils.ValidateTerminalAddress(currentAddress); err != nil {
432 return err
433 }
434
435 args := []interface{}{
436 currentAddress.IP,
437 currentAddress.PrefixLen,
438 currentAddress.Family,
439 currentAddress.TerminalAddressID,
440 }
441
442
443 if _, err = transaction.ExecContext(ctx, sqlquery.TerminalAddressUpdateQuery, args...); err != nil {
444 return err
445 }
446 }
447 return nil
448 }
449
450 func (t *terminalService) DeleteTerminalEntry(ctx context.Context, terminalID string) error {
451 _, err := t.SQLDB.ExecContext(ctx, sqlquery.TerminalDeleteQuery, terminalID)
452 return err
453 }
454
455 func (t *terminalService) CreateTerminalInterfaceEntry(ctx context.Context, terminalID string, newTerminalInterface *model.TerminalInterfaceCreateInput) (*model.TerminalInterface, error) {
456 if err := utils.ValidateTerminalInterfaceCreateInput(newTerminalInterface); err != nil {
457 return nil, err
458 }
459
460 transaction, err := t.SQLDB.BeginTx(ctx, nil)
461 if err != nil {
462 return nil, err
463 }
464 newIfaceList := []*model.TerminalInterfaceCreateInput{newTerminalInterface}
465 ifaceList, err := t.createTerminalInterfaceEntries(ctx, transaction, newIfaceList, terminalID)
466 if err != nil {
467 if rollbackErr := transaction.Rollback(); rollbackErr != nil {
468 return nil, rollbackErr
469 }
470 return nil, err
471 }
472 if err = transaction.Commit(); err != nil {
473 return nil, err
474 }
475 if len(ifaceList) == 0 {
476 return nil, errors.New("no interface to return - potential error creating interface entry")
477 }
478 return ifaceList[0], nil
479 }
480
481 func (t *terminalService) DeleteTerminalInterfaceEntry(ctx context.Context, terminalInterfaceID string) (*model.Terminal, error) {
482 terminal, err := t.GetTerminalFromInterface(ctx, terminalInterfaceID)
483 if err != nil {
484 return nil, err
485 }
486
487 if len(terminal.Interfaces) == 1 {
488 return nil, errors.New("unable to delete the last interface of the terminal")
489 }
490
491 for i, iface := range terminal.Interfaces {
492 if iface.TerminalInterfaceID == terminalInterfaceID {
493 terminal.Interfaces[i] = terminal.Interfaces[len(terminal.Interfaces)-1]
494 terminal.Interfaces = terminal.Interfaces[:len(terminal.Interfaces)-1]
495 break
496 }
497 }
498
499 _, err = t.SQLDB.ExecContext(ctx, sqlquery.TerminalInterfaceDeleteQuery, terminalInterfaceID)
500 return terminal, err
501 }
502
503 func (t *terminalService) CreateTerminalAddressEntry(ctx context.Context, terminalInterfaceID string, newTerminalAddress *model.TerminalAddressCreateInput) (*model.TerminalAddress, error) {
504 address := utils.CreateTerminalAddressModel(uuid.NewString(), newTerminalAddress.IP, newTerminalAddress.PrefixLen, newTerminalAddress.Family, terminalInterfaceID)
505 args := []interface{}{
506 address.TerminalAddressID,
507 address.IP,
508 address.PrefixLen,
509 address.Family,
510 address.TerminalInterfaceID,
511 }
512 _, err := t.SQLDB.ExecContext(ctx, sqlquery.TerminalAddressCreateQuery, args...)
513 if err != nil {
514 return nil, err
515 }
516 return &address, nil
517 }
518
519 func (t *terminalService) UpdateTerminalAddressEntry(ctx context.Context, updatedAddress *model.TerminalAddressIDInput) (*model.TerminalAddress, error) {
520 updateAddresses := []*model.TerminalAddressIDInput{updatedAddress}
521 currentAddr, err := t.getTerminalAddress(ctx, updatedAddress.TerminalAddressID)
522 if err != nil {
523 return nil, err
524 }
525 currentAddrs := []*model.TerminalAddress{currentAddr}
526
527 transaction, err := t.SQLDB.BeginTx(ctx, nil)
528 if err != nil {
529 return nil, err
530 }
531
532 if err := t.updateTerminalAddressEntries(ctx, transaction, updateAddresses, currentAddrs); err != nil {
533 if rollbackErr := transaction.Rollback(); rollbackErr != nil {
534 return nil, rollbackErr
535 }
536 return nil, err
537 }
538
539 if err := transaction.Commit(); err != nil {
540 return nil, err
541 }
542
543 addr, err := t.getTerminalAddress(ctx, updatedAddress.TerminalAddressID)
544 if err != nil {
545 return nil, err
546 }
547 return addr, nil
548 }
549
550 func (t *terminalService) DeleteTerminalAddressEntry(ctx context.Context, terminalAddressID string) (*model.Terminal, error) {
551 terminal, err := t.GetTerminalFromAddress(ctx, terminalAddressID)
552 if err != nil {
553 return nil, err
554 }
555
556 out:
557 for _, iface := range terminal.Interfaces {
558 for i, address := range iface.Addresses {
559 if address.TerminalAddressID == terminalAddressID {
560 iface.Addresses[i] = iface.Addresses[len(iface.Addresses)-1]
561 iface.Addresses = iface.Addresses[:len(iface.Addresses)-1]
562
563
564 if err := utils.ValidateAllTerminalAddresses(iface.Dhcp4, iface.Addresses); err != nil {
565 return nil, err
566 }
567 break out
568 }
569 }
570 }
571
572 _, err = t.SQLDB.ExecContext(ctx, sqlquery.TerminalAddressDeleteQuery, terminalAddressID)
573 return terminal, err
574 }
575
576 func (t *terminalService) GetTerminal(ctx context.Context, terminalID string, getLabel *bool) (*model.Terminal, error) {
577 row := t.SQLDB.QueryRowContext(ctx, sqlquery.GetTerminalByIDQuery, terminalID)
578 terminal, err := t.scanTerminalRow(row)
579 if err != nil {
580 return nil, err
581 }
582
583 terminal.Interfaces, err = t.getTerminalInterfaceByTerminalID(ctx, &terminalID)
584 if err != nil {
585 return nil, err
586 }
587
588 terminal.Disks, err = t.getTerminalDisks(ctx, &terminalID)
589 if err != nil {
590 return nil, err
591 }
592
593 if getLabel != nil && *getLabel {
594 terminalLabelSvc := NewTerminalLabelService(t.SQLDB, nil, nil, nil, t.LabelService)
595 terminalLabels, err := terminalLabelSvc.GetTerminalLabels(ctx, model.SearchTerminalLabelInput{
596 TerminalID: &terminalID,
597 })
598 if err != nil {
599 return nil, err
600 }
601 terminal.Labels = terminalLabels
602 }
603
604 term, err := t.updateTerminalVersions(ctx, []*model.Terminal{terminal})
605 if err != nil {
606 return nil, err
607 }
608 terminal = term[0]
609
610 return terminal, nil
611 }
612
613 func (t *terminalService) GetTerminals(ctx context.Context, clusterEdgeID *string, terminalHostname *string) ([]*model.Terminal, error) {
614 switch {
615 case clusterEdgeID == nil && terminalHostname == nil:
616 return t.getAllTerminals(ctx)
617 case clusterEdgeID != nil && terminalHostname == nil:
618 return t.getClusterEdgeIDTerminals(ctx, *clusterEdgeID)
619 case clusterEdgeID == nil && terminalHostname != nil:
620 return t.getHostnameTerminals(ctx, *terminalHostname)
621 default:
622 return t.getClusterEdgeIDAndHostnameTerminals(ctx, *clusterEdgeID, *terminalHostname)
623 }
624 }
625
626 func (t *terminalService) GetNodeReplicationStatus(ctx context.Context, clusterEdgeID, hostname string) (*model.ReplicationStatus, error) {
627 var name string
628 row := t.SQLDB.QueryRowContext(ctx, sqlquery.GetNodeReplicaName, clusterEdgeID, fmt.Sprintf("%q", hostname))
629 if err := row.Scan(&name); err != nil {
630 if errors.Is(err, sql.ErrNoRows) {
631 return &model.ReplicationStatus{
632 Status: "NotFound",
633 }, nil
634 }
635 return nil, sqlerr.Wrap(err)
636 }
637 row = t.SQLDB.QueryRowContext(ctx, sqlquery.GetReplicationStatusByName, clusterEdgeID, name)
638 var replicationStatus model.ReplicationStatus
639 if err := row.Scan(&replicationStatus.Name, &replicationStatus.Status); err != nil {
640 if errors.Is(err, sql.ErrNoRows) {
641 return &model.ReplicationStatus{
642 Status: "NotFound",
643 }, nil
644 }
645 return nil, sqlerr.Wrap(err)
646 }
647 replicationStatus.Status = strings.Trim(replicationStatus.Status, "\"")
648 return &replicationStatus, nil
649 }
650
651 func (t *terminalService) GetTerminalNodeStatus(ctx context.Context, clusterEdgeID, terminalID, hostname string) (*model.TerminalStatus, error) {
652 var (
653 nodeReady = ""
654 ieNodeReady = ""
655 terminalStatus = &model.TerminalStatus{}
656 notReported = make(map[string]bool)
657 )
658 row := t.SQLDB.QueryRowContext(ctx, sqlquery.GetTerminalNodeStatus, "Node", clusterEdgeID, hostname, false)
659 err := row.Scan(&nodeReady)
660 if err != nil && !errors.Is(err, sql.ErrNoRows) {
661 return nil, err
662 }
663
664
665 if nodeReady == status.Unknown || errors.Is(err, sql.ErrNoRows) {
666 notReported["Node"] = true
667 }
668 row = t.SQLDB.QueryRowContext(ctx, sqlquery.GetTerminalNodeStatus, v1ien.IENodeGVK.Kind, clusterEdgeID, hostname, false)
669 if err := row.Scan(&ieNodeReady); err != nil {
670 if !errors.Is(err, sql.ErrNoRows) {
671 return nil, err
672 }
673 notReported[v1ien.IENodeGVK.Kind] = true
674 }
675
676
677
678 if nodeReady == status.IsReady && ieNodeReady == status.IsReady {
679 terminalStatus.Status = status.Ready
680 terminalStatus.Message = status.NodeReadyMessage
681 return terminalStatus, nil
682 }
683
684
685
686 if nodeReady == status.NotReady || ieNodeReady == status.NotReady {
687 aggregatedErr := make([]string, 0)
688 if nodeReady == status.NotReady {
689 nodeErrMessage := ""
690 if err := t.getNodeErrorMessage(ctx, clusterEdgeID, "Node", hostname, &nodeErrMessage, &aggregatedErr); err != nil {
691 return nil, err
692 }
693 }
694 if ieNodeReady == status.NotReady {
695 ieNodeErrMessage := ""
696 if err := t.getNodeErrorMessage(ctx, clusterEdgeID, v1ien.IENodeGVK.Kind, hostname, &ieNodeErrMessage, &aggregatedErr); err != nil {
697 return nil, err
698 }
699 }
700 terminalStatus.Status = status.Error
701 terminalStatus.Message = status.MergeErrorMessages(aggregatedErr)
702 return terminalStatus, nil
703 }
704 activationCode := ""
705 row = t.SQLDB.QueryRowContext(ctx, edgenode.GetActivationCode, terminalID)
706 if err := row.Scan(&activationCode); err != nil {
707 return nil, err
708 }
709 if status.IsNotReported("Node", notReported) || status.IsNotReported(v1ien.IENodeGVK.Kind, notReported) {
710 switch {
711 case status.IsNotReported("Node", notReported) && status.IsNotReported(v1ien.IENodeGVK.Kind, notReported) && activationCode == "":
712
713
714
715 terminalStatus.Status = status.NotAvailable
716 terminalStatus.Message = status.NotAvailableMessage
717 case activationCode != "":
718
719
720
721 terminalStatus.Status = status.AwaitingInstallation
722 terminalStatus.Message = status.UnusedActivationCodeMessage
723
724
725
726 case activationCode == "":
727 terminalStatus.Status = status.Disconnected
728 terminalStatus.Message = status.DisconnectedMessage
729 }
730 }
731 return terminalStatus, nil
732 }
733
734 func (t *terminalService) getNodeErrorMessage(ctx context.Context, clusterEdgeID, kind, hostName string, errMessage *string, aggregatedErr *[]string) error {
735 row := t.SQLDB.QueryRowContext(ctx, sqlquery.GetTerminalNodeStatusMessage, kind, clusterEdgeID, hostName, false)
736 if err := row.Scan(errMessage); err != nil {
737 if !errors.Is(err, sql.ErrNoRows) {
738 return err
739 }
740 } else {
741 *aggregatedErr = append(*aggregatedErr, *errMessage)
742 }
743 return nil
744 }
745
746 func (t *terminalService) GetBannerTerminals(ctx context.Context, bannerEdgeID string, projectID string) ([]*model.Terminal, error) {
747
748 rows, err := t.SQLDB.QueryContext(ctx, sqlquery.GetClustersByBannerEdgeIDQuery, bannerEdgeID)
749 if err != nil {
750 return nil, err
751 }
752
753
754 versionMap, err := t.getTerminalVersionMapFromProjectID(ctx, projectID)
755 if err != nil {
756 return nil, err
757 }
758
759 terminalMap := make(map[string]*model.Terminal)
760 ifaceMap := make(map[string]*model.TerminalInterface)
761 addressMap := make(map[string]*model.TerminalAddress)
762
763 for rows.Next() {
764 terminal, ifaceBanner, addressBanner := model.Terminal{}, utils.NullIface{}, utils.NullAddress{}
765 err := rows.Scan(&terminal.TerminalID, &terminal.Lane, &terminal.Role, &terminal.Class, &terminal.DiscoverDisks, &terminal.BootDisk, &terminal.PrimaryInterface, &terminal.ExistingEfiPart, &terminal.SwapEnabled, &terminal.ClusterEdgeID, &terminal.ClusterName, &terminal.Hostname,
766 &ifaceBanner.TerminalInterfaceID, &ifaceBanner.MacAddress, &ifaceBanner.Dhcp4, &ifaceBanner.Dhcp6, &ifaceBanner.Gateway4, &ifaceBanner.Gateway6, &ifaceBanner.TerminalID,
767 &addressBanner.TerminalAddressID, &addressBanner.IP, &addressBanner.PrefixLen, &addressBanner.Family, &addressBanner.TerminalInterfaceID)
768 if err != nil {
769 return nil, err
770 }
771
772 if _, exists := terminalMap[terminal.TerminalID]; !exists {
773 terminalMap[terminal.TerminalID] = &terminal
774 }
775 if ifaceBanner.TerminalInterfaceID.Valid {
776 iface, err := utils.ConvertNullIface(ifaceBanner)
777 if err != nil {
778 return nil, err
779 }
780 if _, exists := ifaceMap[iface.TerminalInterfaceID]; !exists {
781 ifaceMap[iface.TerminalInterfaceID] = iface
782 }
783 }
784 if addressBanner.TerminalAddressID.Valid {
785 address, err := utils.ConvertNullAddr(addressBanner)
786 if err != nil {
787 return nil, err
788 }
789 if _, exists := addressMap[address.TerminalAddressID]; !exists {
790 addressMap[address.TerminalAddressID] = address
791 }
792 }
793 }
794
795 if err := rows.Err(); err != nil {
796 return nil, sqlerr.Wrap(err)
797 }
798
799 terminals := mapper.UnpackBannerTerminals(terminalMap, ifaceMap, addressMap, versionMap)
800 for _, terminal := range terminals {
801 terminalDisks, err := t.getTerminalDisks(ctx, &terminal.TerminalID)
802 if err != nil {
803 return nil, err
804 }
805 terminal.Disks = terminalDisks
806 }
807
808 return terminals, nil
809 }
810
811 func (t *terminalService) getAllTerminals(ctx context.Context) ([]*model.Terminal, error) {
812 rows, err := t.SQLDB.QueryContext(ctx, sqlquery.GetAllTerminalsQuery)
813 if err != nil {
814 return nil, err
815 }
816
817 terminals, err := t.scanTerminalRows(rows)
818 if err != nil {
819 return nil, err
820 }
821
822 for _, terminal := range terminals {
823 terminalDisks, err := t.getTerminalDisks(ctx, &terminal.TerminalID)
824 if err != nil {
825 return nil, err
826 }
827 terminal.Disks = terminalDisks
828
829 terminal.Interfaces, err = t.getTerminalInterfaceByTerminalID(ctx, &terminal.TerminalID)
830 if err != nil {
831 return nil, err
832 }
833
834 terminalLabelSvc := NewTerminalLabelService(t.SQLDB, nil, nil, nil, t.LabelService)
835 terminalLabels, err := terminalLabelSvc.GetTerminalLabels(ctx, model.SearchTerminalLabelInput{
836 TerminalID: &terminal.TerminalID,
837 })
838 if err != nil {
839 return nil, err
840 }
841 terminal.Labels = terminalLabels
842 }
843
844 return t.updateTerminalVersions(ctx, terminals)
845 }
846
847 func (t *terminalService) getClusterEdgeIDTerminals(ctx context.Context, clusterEdgeID string) ([]*model.Terminal, error) {
848 terminals, err := t.GetTerminalsByClusterID(ctx, clusterEdgeID)
849 if err != nil {
850 return nil, err
851 }
852
853 for _, terminal := range terminals {
854 terminalDisks, err := t.getTerminalDisks(ctx, &terminal.TerminalID)
855 if err != nil {
856 return nil, err
857 }
858 terminal.Disks = terminalDisks
859
860 terminal.Interfaces, err = t.getTerminalInterfaceByTerminalID(ctx, &terminal.TerminalID)
861 if err != nil {
862 return nil, err
863 }
864
865 terminalLabelSvc := NewTerminalLabelService(t.SQLDB, nil, nil, nil, t.LabelService)
866 terminalLabels, err := terminalLabelSvc.GetTerminalLabels(ctx, model.SearchTerminalLabelInput{
867 TerminalID: &terminal.TerminalID,
868 })
869 if err != nil {
870 return nil, err
871 }
872 terminal.Labels = terminalLabels
873 }
874
875 return t.updateTerminalVersions(ctx, terminals)
876 }
877
878 func (t *terminalService) GetTerminalsByClusterID(ctx context.Context, clusterEdgeID string) ([]*model.Terminal, error) {
879 rows, err := t.SQLDB.QueryContext(ctx, sqlquery.GetTerminalByClusterEdgeIDQuery, clusterEdgeID)
880 if err != nil {
881 return nil, err
882 }
883
884 terminals, err := t.scanTerminalRows(rows)
885 if err != nil {
886 return nil, err
887 }
888 return terminals, nil
889 }
890
891 func (t *terminalService) getHostnameTerminals(ctx context.Context, terminalHostname string) ([]*model.Terminal, error) {
892 rows, err := t.SQLDB.QueryContext(ctx, sqlquery.GetTerminalByHostnameQuery, terminalHostname)
893 if err != nil {
894 return nil, err
895 }
896
897 terminals, err := t.scanTerminalRows(rows)
898 if err != nil {
899 return nil, err
900 }
901
902 for _, terminal := range terminals {
903 terminalDisks, err := t.getTerminalDisks(ctx, &terminal.TerminalID)
904 if err != nil {
905 return nil, err
906 }
907 terminal.Disks = terminalDisks
908
909 terminal.Interfaces, err = t.getTerminalInterfaceByTerminalID(ctx, &terminal.TerminalID)
910 if err != nil {
911 return nil, err
912 }
913
914 terminalLabelSvc := NewTerminalLabelService(t.SQLDB, nil, nil, nil, t.LabelService)
915 terminalLabels, err := terminalLabelSvc.GetTerminalLabels(ctx, model.SearchTerminalLabelInput{
916 TerminalID: &terminal.TerminalID,
917 })
918 if err != nil {
919 return nil, err
920 }
921 terminal.Labels = terminalLabels
922 }
923
924 return t.updateTerminalVersions(ctx, terminals)
925 }
926
927 func (t *terminalService) getClusterEdgeIDAndHostnameTerminals(ctx context.Context, clusterEdgeID string, terminalHostname string) ([]*model.Terminal, error) {
928 rows, err := t.SQLDB.QueryContext(ctx, sqlquery.GetTerminalByClusterEdgeIDAndHostnameQuery, clusterEdgeID, terminalHostname)
929 if err != nil {
930 return nil, err
931 }
932
933 terminals, err := t.scanTerminalRows(rows)
934 if err != nil {
935 return nil, err
936 }
937
938 for _, terminal := range terminals {
939 terminalDisks, err := t.getTerminalDisks(ctx, &terminal.TerminalID)
940 if err != nil {
941 return nil, err
942 }
943 terminal.Disks = terminalDisks
944
945 terminal.Interfaces, err = t.getTerminalInterfaceByTerminalID(ctx, &terminal.TerminalID)
946 if err != nil {
947 return nil, err
948 }
949
950 terminalLabelSvc := NewTerminalLabelService(t.SQLDB, nil, nil, nil, t.LabelService)
951 terminalLabels, err := terminalLabelSvc.GetTerminalLabels(ctx, model.SearchTerminalLabelInput{
952 TerminalID: &terminal.TerminalID,
953 })
954 if err != nil {
955 return nil, err
956 }
957 terminal.Labels = terminalLabels
958 }
959
960 return t.updateTerminalVersions(ctx, terminals)
961 }
962
963 func (t *terminalService) getTerminalInterface(ctx context.Context, interfaceID string) (*model.TerminalInterface, error) {
964 row := t.SQLDB.QueryRowContext(ctx, sqlquery.GetTerminalInterfaceQuery, interfaceID)
965 iface, err := t.scanTerminalIfaceRow(row)
966 if err != nil {
967 return nil, err
968 }
969
970 iface.Addresses, err = t.getTerminalAddressByInterfaceID(ctx, &iface.TerminalInterfaceID)
971 if err != nil {
972 return nil, err
973 }
974
975 return iface, nil
976 }
977
978 func (t *terminalService) getTerminalInterfaceByTerminalID(ctx context.Context, terminalID *string) ([]*model.TerminalInterface, error) {
979 rows, err := t.SQLDB.QueryContext(ctx, sqlquery.GetTerminalInterfaceByTerminalIDQuery, terminalID)
980 if err != nil {
981 return nil, err
982 }
983
984 ifaces, err := t.scanTerminalIfaceRows(rows)
985 if err != nil {
986 return nil, err
987 }
988
989 for _, iface := range ifaces {
990 iface.Addresses, err = t.getTerminalAddressByInterfaceID(ctx, &iface.TerminalInterfaceID)
991 if err != nil {
992 return nil, err
993 }
994 }
995
996 return ifaces, nil
997 }
998
999 func (t *terminalService) getTerminalAddress(ctx context.Context, addressID string) (*model.TerminalAddress, error) {
1000 row := t.SQLDB.QueryRowContext(ctx, sqlquery.GetTerminalAddressQuery, addressID)
1001 addr, err := t.scanTerminalAddressRow(row)
1002 if err != nil {
1003 return nil, err
1004 }
1005 return addr, nil
1006 }
1007
1008 func (t *terminalService) getTerminalAddressByInterfaceID(ctx context.Context, terminalInterfaceID *string) ([]*model.TerminalAddress, error) {
1009 rows, err := t.SQLDB.QueryContext(ctx, sqlquery.GetTerminalAddressByInterfaceIDQuery, terminalInterfaceID)
1010 if err != nil {
1011 return nil, err
1012 }
1013
1014 addresses, err := t.scanTerminalAddressRows(rows)
1015 if err != nil {
1016 return nil, err
1017 }
1018
1019 return addresses, nil
1020 }
1021
1022 func (t *terminalService) scanTerminalRow(row *sql.Row) (*model.Terminal, error) {
1023 terminal := &model.Terminal{}
1024 err := row.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)
1025 if err != nil {
1026 return nil, err
1027 }
1028 return terminal, nil
1029 }
1030
1031 func (t *terminalService) scanTerminalRows(rows *sql.Rows) ([]*model.Terminal, error) {
1032 terminals := []*model.Terminal{}
1033 for rows.Next() {
1034 terminal := &model.Terminal{}
1035 err := rows.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)
1036 if err != nil {
1037 return nil, err
1038 }
1039 terminals = append(terminals, terminal)
1040 }
1041 if err := rows.Err(); err != nil {
1042 return nil, sqlerr.Wrap(err)
1043 }
1044 return terminals, nil
1045 }
1046
1047 func (t *terminalService) scanTerminalIfaceRow(row *sql.Row) (*model.TerminalInterface, error) {
1048 iface := &model.TerminalInterface{}
1049 if err := row.Scan(&iface.TerminalInterfaceID, &iface.MacAddress, &iface.Dhcp4, &iface.Dhcp6, &iface.Gateway4, &iface.Gateway6, &iface.TerminalID); err != nil {
1050 return nil, err
1051 }
1052 return iface, nil
1053 }
1054
1055 func (t *terminalService) scanTerminalIfaceRows(rows *sql.Rows) ([]*model.TerminalInterface, error) {
1056 terminalInterfaces := []*model.TerminalInterface{}
1057 for rows.Next() {
1058 terminalInterface := model.TerminalInterface{}
1059 err := rows.Scan(&terminalInterface.TerminalInterfaceID, &terminalInterface.MacAddress, &terminalInterface.Dhcp4, &terminalInterface.Dhcp6, &terminalInterface.Gateway4, &terminalInterface.Gateway6, &terminalInterface.TerminalID)
1060 if err != nil {
1061 return nil, err
1062 }
1063 terminalInterfaces = append(terminalInterfaces, &terminalInterface)
1064 }
1065 if err := rows.Err(); err != nil {
1066 return nil, sqlerr.Wrap(err)
1067 }
1068 return terminalInterfaces, nil
1069 }
1070
1071 func (t *terminalService) scanTerminalAddressRow(row *sql.Row) (*model.TerminalAddress, error) {
1072 addr := &model.TerminalAddress{}
1073 if err := row.Scan(&addr.TerminalAddressID, &addr.IP, &addr.PrefixLen, &addr.Family, &addr.TerminalInterfaceID); err != nil {
1074 return nil, err
1075 }
1076 return addr, nil
1077 }
1078
1079 func (t *terminalService) scanTerminalAddressRows(rows *sql.Rows) ([]*model.TerminalAddress, error) {
1080 terminalAddresses := []*model.TerminalAddress{}
1081 for rows.Next() {
1082 terminalAddress := model.TerminalAddress{}
1083 if err := rows.Scan(&terminalAddress.TerminalAddressID, &terminalAddress.IP, &terminalAddress.PrefixLen, &terminalAddress.Family, &terminalAddress.TerminalInterfaceID); err != nil {
1084 return nil, err
1085 }
1086 terminalAddresses = append(terminalAddresses, &terminalAddress)
1087 }
1088 if err := rows.Err(); err != nil {
1089 return nil, sqlerr.Wrap(err)
1090 }
1091 return terminalAddresses, nil
1092 }
1093
1094 func (t *terminalService) CreateDSDSIENodeCR(terminal *model.Terminal, clusterNetworkServices []*model.ClusterNetworkServiceInfo, customLabels map[string]string, edgeVersion string) (string, error) {
1095 dsdsIENode := mapper.TerminalToIENode(terminal, clusterNetworkServices, customLabels, edgeVersion)
1096 dsdsIENodeBase64, err := utils.ConvertStructToBase64(dsdsIENode)
1097 if err != nil {
1098 return "", err
1099 }
1100 return dsdsIENodeBase64, nil
1101 }
1102
1103 func (t *terminalService) CreateCICIENodeCR(terminal *model.Terminal, clusterNetworkServices []*model.ClusterNetworkServiceInfo, customLabels map[string]string, edgeVersion string) (string, error) {
1104 terminalCR := mapper.TerminalToCICIENode(terminal, clusterNetworkServices, customLabels, edgeVersion)
1105 terminalCRBase64, err := utils.ConvertStructToBase64(terminalCR)
1106 if err != nil {
1107 return "", err
1108 }
1109 return terminalCRBase64, nil
1110 }
1111
1112 func (t *terminalService) GetTerminalFromInterface(ctx context.Context, terminalInterfaceID string) (*model.Terminal, error) {
1113 row := t.SQLDB.QueryRowContext(ctx, sqlquery.GetTerminalIDFromInterfaceQuery, terminalInterfaceID)
1114 var terminalID string
1115 if err := row.Scan(&terminalID); err != nil {
1116 return nil, err
1117 }
1118
1119 terminal, err := t.GetTerminal(ctx, terminalID, &getLabel)
1120 if err != nil {
1121 return nil, err
1122 }
1123
1124 return terminal, nil
1125 }
1126
1127 func (t *terminalService) GetTerminalFromAddress(ctx context.Context, terminalAddressID string) (*model.Terminal, error) {
1128 row := t.SQLDB.QueryRowContext(ctx, sqlquery.GetInterfaceIDFromAddressQuery, terminalAddressID)
1129 var ifaceID string
1130 if err := row.Scan(&ifaceID); err != nil {
1131 return nil, err
1132 }
1133
1134 row = t.SQLDB.QueryRowContext(ctx, sqlquery.GetTerminalIDFromInterfaceQuery, ifaceID)
1135 var terminalID string
1136 if err := row.Scan(&terminalID); err != nil {
1137 return nil, err
1138 }
1139
1140 terminal, err := t.GetTerminal(ctx, terminalID, &getLabel)
1141 if err != nil {
1142 return nil, err
1143 }
1144
1145 return terminal, nil
1146 }
1147
1148 func (t *terminalService) TerminalDevices(ctx context.Context, terminalID string) (*model.TerminalDevices, error) {
1149 fetchLabels := false
1150 terminal, err := t.GetTerminal(ctx, terminalID, &fetchLabels)
1151 if err != nil {
1152 return &model.TerminalDevices{}, err
1153 }
1154
1155 var value string
1156 row := t.SQLDB.QueryRowContext(ctx, sqlquery.GetTerminalDevices, terminal.Hostname, terminal.ClusterEdgeID)
1157 if err := row.Scan(&value); err != nil {
1158 return &model.TerminalDevices{}, err
1159 }
1160
1161 deviceMap := dsv1.DeviceStatusesSpec{Devices: map[string][]dsv1.DeviceState{}}
1162 if err = json.Unmarshal([]byte(value), &deviceMap.Devices); err != nil {
1163 return &model.TerminalDevices{}, fmt.Errorf("failed to convert device map: %v", err)
1164 }
1165
1166 terminalDevices := &model.TerminalDevices{Classes: make([]*model.ClassDeviceMap, len(deviceMap.Devices))}
1167
1168 classIdx := 0
1169 for class, classDevices := range deviceMap.Devices {
1170 devices := make([]*model.Device, len(classDevices))
1171 devIdx := 0
1172 for _, dev := range classDevices {
1173 devices[devIdx] = &model.Device{Name: dev.Name}
1174 devIdx++
1175 }
1176
1177 terminalDevices.Classes[classIdx] = &model.ClassDeviceMap{
1178 Name: class,
1179 Devices: devices,
1180 }
1181 classIdx++
1182 }
1183 return terminalDevices, nil
1184 }
1185
1186 func (t *terminalService) checkHostnames(ctx context.Context, nodeName string, newTerminal *model.TerminalCreateInput) error {
1187
1188 rows, err := t.SQLDB.QueryContext(ctx, sqlquery.GetAllHostnamesForAClusterQuery, newTerminal.ClusterEdgeID)
1189 if err != nil {
1190 return err
1191 }
1192
1193 var hostname string
1194 for rows.Next() {
1195 err := rows.Scan(&hostname)
1196 if err != nil {
1197 return err
1198 }
1199 if hostname == nodeName {
1200 return fmt.Errorf("cannot create terminal - hostname %s already exists", hostname)
1201 }
1202 }
1203 if err := rows.Err(); err != nil {
1204 return sqlerr.Wrap(err)
1205 }
1206 return nil
1207 }
1208
1209 func (t *terminalService) RemoveClusterEdgeBootstrapToken(ctx context.Context, clusterEdgeID string) error {
1210 if clusterEdgeID == "" {
1211 return nil
1212 }
1213 return t.uploadClusterEdgeBootstrapToken(ctx, "", clusterEdgeID)
1214 }
1215
1216 func (t *terminalService) uploadClusterEdgeBootstrapToken(ctx context.Context, edgeBootstrapTokenHash, clusterEdgeID string) error {
1217 expirationTime := time.Now().UTC().Add(time.Hour * 2).Format(mapper.TimeFormat)
1218 _, err := t.SQLDB.ExecContext(ctx, sqlquery.UploadEdgeBootstrapToken, edgeBootstrapTokenHash, expirationTime, clusterEdgeID)
1219 return err
1220 }
1221
1222 func (t *terminalService) checkLaneDuplicates(ctx context.Context, newLane, clusterEdgeID string) error {
1223
1224 rows, err := t.SQLDB.QueryContext(ctx, sqlquery.GetAllLanesForAClusterQuery, clusterEdgeID)
1225 if err != nil {
1226 return err
1227 }
1228
1229 for rows.Next() {
1230 var hostname string
1231 var lane sql.NullString
1232 if err := rows.Scan(&lane, &hostname); err != nil {
1233 return err
1234 }
1235 if lane.Valid && lane.String == newLane {
1236 return fmt.Errorf("cannot create terminal - lane %s already in use by terminal %s", newLane, hostname)
1237 }
1238 }
1239 if err := rows.Err(); err != nil {
1240 return sqlerr.Wrap(err)
1241 }
1242 return nil
1243 }
1244
1245 func (t *terminalService) GetTerminalBootstrapConfig(ctx context.Context, terminal *model.Terminal, clusterNetworkServices []*model.ClusterNetworkServiceInfo, breakGlassSecret, grubSecret cc.Secret, bootstrapAck, isFirstNode bool, organization string, bootstrapTokenValues []*model.KeyValues, clusterCaHash, endpoint string) (string, error) {
1246 edgeInfraVersion := version.New().SemVer
1247
1248 grubEntry := strings.Split(grubSecret.String(), "\n")
1249 if len(grubEntry) != 3 {
1250 return "", errors.New("invalid grub credential returned to bootstrap config")
1251 }
1252
1253 grubLine := strings.Split(grubEntry[1], " ")
1254 if len(grubEntry) != 3 {
1255 return "", errors.New("invalid grub hash returned to bootstrap config")
1256 }
1257
1258 payload := mapper.TerminalBootstrapPayload{
1259 Terminal: terminal,
1260 EdgeInfraVersion: edgeInfraVersion,
1261 ClusterNetworkServices: clusterNetworkServices,
1262 ShadowFileLine: breakGlassSecret.String(),
1263 GrubFileEntry: grubLine[2],
1264 FirstNode: isFirstNode,
1265 BootstrapTokenValues: bootstrapTokenValues,
1266 ClusterCaHash: clusterCaHash,
1267 BootstrapAck: bootstrapAck,
1268 Organization: organization,
1269 Endpoint: endpoint,
1270 }
1271
1272 if isFirstNode {
1273 edgeBootstrapToken, err := crypto.GenerateRandomEdgeBootstrapToken()
1274 if err != nil {
1275 return "", err
1276 }
1277 if err := t.uploadClusterEdgeBootstrapToken(ctx, hex.EncodeToString(edgeBootstrapToken.Hashed()), terminal.ClusterEdgeID); err != nil {
1278 return "", err
1279 }
1280 payload.EdgeBootstrapToken = edgeBootstrapToken.Plain()
1281 }
1282
1283 config, err := payload.Yaml()
1284 if err != nil {
1285 return "", err
1286 }
1287
1288 yaml, err := yaml.Marshal(&config)
1289 if err != nil {
1290 return "", err
1291 }
1292
1293 return string(yaml), nil
1294 }
1295
1296 func (t *terminalService) getProjectIDbyClusterEdgeID(ctx context.Context, clusterEdgeID string) (string, error) {
1297 var projectID string
1298 row := t.SQLDB.QueryRowContext(ctx, sqlquery.GetProjectIDByClusterEdgeID, clusterEdgeID)
1299 if err := row.Scan(&projectID); err != nil {
1300 return "", err
1301 }
1302 return projectID, nil
1303 }
1304
1305 func (t *terminalService) getNodeResources(ctx context.Context, projectID string) ([]string, error) {
1306
1307 logRequest := mapper.GetNodes()
1308 logRequest.GetClusterEdgeID = true
1309 return t.BQClient.GetKubeResource(ctx, projectID, nil, logRequest)
1310 }
1311
1312 func (t *terminalService) getTerminalVersionMapFromProjectID(ctx context.Context, projectID string) (map[string]map[string]string, error) {
1313
1314 nodes, err := t.getNodeResources(ctx, projectID)
1315 if err != nil {
1316 return nil, err
1317 }
1318 return mapper.GenerateTerminalVersionMap(nodes)
1319 }
1320
1321 func (t *terminalService) updateTerminalVersions(ctx context.Context, terminals []*model.Terminal) ([]*model.Terminal, error) {
1322 if len(terminals) == 0 {
1323 return terminals, nil
1324 }
1325 projectID, err := t.getProjectIDbyClusterEdgeID(ctx, terminals[0].ClusterEdgeID)
1326 if err != nil {
1327 return nil, err
1328 }
1329 versionMap, err := t.getTerminalVersionMapFromProjectID(ctx, projectID)
1330
1331 if err != nil {
1332 return nil, err
1333 }
1334
1335 for _, terminal := range terminals {
1336 terminal.Version = versionMap[terminal.ClusterEdgeID][terminal.Hostname]
1337 }
1338 return terminals, nil
1339 }
1340
1341 func NewTerminalService(sqlDB *sql.DB, labelSvc LabelService) TerminalService {
1342 return &terminalService{
1343 SQLDB: sqlDB,
1344 LabelService: labelSvc,
1345 }
1346 }
1347
1348 func NewTerminalServiceBQ(sqlDB *sql.DB, BQClient clients.BQClient, labelSvc LabelService) TerminalService {
1349 return &terminalService{
1350 SQLDB: sqlDB,
1351 LabelService: labelSvc,
1352 BQClient: BQClient,
1353 }
1354 }
1355
View as plain text