-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcheck_and_finalize.go
More file actions
148 lines (122 loc) · 5.29 KB
/
check_and_finalize.go
File metadata and controls
148 lines (122 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package elasticsearch
import (
"fmt"
"github.com/spf13/cobra"
"github.com/stackvista/stackstate-backup-cli/cmd/cmdutils"
"github.com/stackvista/stackstate-backup-cli/internal/app"
es "github.com/stackvista/stackstate-backup-cli/internal/clients/elasticsearch"
"github.com/stackvista/stackstate-backup-cli/internal/foundation/config"
"github.com/stackvista/stackstate-backup-cli/internal/orchestration/portforward"
"github.com/stackvista/stackstate-backup-cli/internal/orchestration/restore"
"github.com/stackvista/stackstate-backup-cli/internal/orchestration/scale"
)
// Check-and-finalize command flags
var (
checkOperationID string
checkWait bool
)
func checkAndFinalizeCmd(globalFlags *config.CLIGlobalFlags) *cobra.Command {
cmd := &cobra.Command{
Use: "check-and-finalize",
Short: "Check restore status and finalize if complete",
Long: `Check the status of a restore operation and perform finalization (scale up deployments) if complete.
If the restore is still running and --wait is specified, wait for completion before finalizing.`,
Run: func(_ *cobra.Command, _ []string) {
cmdutils.Run(globalFlags, runCheckAndFinalize, cmdutils.MinioIsRequired)
},
}
cmd.Flags().StringVar(&checkOperationID, "operation-id", "", "Operation ID of the restore operation (required)")
cmd.Flags().BoolVar(&checkWait, "wait", false, "Wait for restore to complete if still running")
_ = cmd.MarkFlagRequired("operation-id")
return cmd
}
func runCheckAndFinalize(appCtx *app.Context) error {
// Setup port-forward to Elasticsearch
serviceName := appCtx.Config.Elasticsearch.Service.Name
remotePort := appCtx.Config.Elasticsearch.Service.Port
pf, err := portforward.SetupPortForward(appCtx.K8sClient, appCtx.Namespace, serviceName, remotePort, appCtx.Logger)
if err != nil {
return err
}
defer close(pf.StopChan)
// Create ES client with actual port
esClient, err := appCtx.NewESClient(pf.LocalPort)
if err != nil {
return fmt.Errorf("failed to create Elasticsearch client: %w", err)
}
repository := appCtx.Config.Elasticsearch.Restore.Repository
return checkAndFinalize(esClient, appCtx, repository, checkOperationID, checkWait)
}
func checkAndFinalize(esClient es.Interface, appCtx *app.Context, repository, snapshotName string, waitForComplete bool) error {
// Get restore status
appCtx.Logger.Infof("Checking restore status for snapshot: %s", snapshotName)
status, isComplete, err := esClient.GetRestoreStatus(repository, snapshotName)
if err != nil {
return fmt.Errorf("failed to get restore status: %w", err)
}
appCtx.Logger.Debugf("Restore status: %s (complete: %v)", status, isComplete)
// Handle different scenarios
if isComplete {
switch status {
case "SUCCESS":
appCtx.Logger.Successf("Restore completed successfully")
return finalizeRestore(appCtx)
case "NOT_FOUND":
appCtx.Logger.Infof("No restore operation found for snapshot: %s", snapshotName)
appCtx.Logger.Infof("The restore may have already been finalized")
appCtx.Logger.Println()
appCtx.Logger.Infof("Checking if deployments need to be scaled up...")
return attemptScaleUp(appCtx)
case "FAILED":
return fmt.Errorf("restore failed with status: %s", status)
default:
return fmt.Errorf("restore completed with unexpected status: %s", status)
}
}
// Restore still running
appCtx.Logger.Infof("Restore is still in progress (status: %s)", status)
if waitForComplete {
appCtx.Logger.Println()
return waitAndFinalize(esClient, appCtx, repository, snapshotName)
}
// Not waiting - print status and exit
appCtx.Logger.Println()
restore.PrintAPIRunningRestoreStatus("elasticsearch", snapshotName, appCtx.Namespace, appCtx.Logger)
return nil
}
// waitAndFinalize waits for restore to complete and finalizes (scale up)
func waitAndFinalize(esClient es.Interface, appCtx *app.Context, repository, snapshotName string) error {
restore.PrintAPIWaitingMessage("elasticsearch", snapshotName, appCtx.Namespace, appCtx.Logger)
// Wait for restore to complete
checkStatusFn := func() (string, bool, error) {
return esClient.GetRestoreStatus(repository, snapshotName)
}
if err := restore.WaitForAPIRestore(checkStatusFn, 0, appCtx.Logger); err != nil {
return err
}
// Finalize restore (scale up)
return finalizeRestore(appCtx)
}
// finalizeRestore performs post-restore finalization (scale up deployments and release lock)
func finalizeRestore(appCtx *app.Context) error {
appCtx.Logger.Println()
labelSelector := appCtx.Config.Elasticsearch.Restore.ScaleDownLabelSelector
scaleUpFn := func() error {
return scale.ScaleUpAndReleaseLock(appCtx.K8sClient, appCtx.Namespace, labelSelector, appCtx.Logger)
}
return restore.FinalizeRestore(scaleUpFn, appCtx.Logger)
}
// attemptScaleUp tries to scale up deployments and release lock (used when restore is not found/already complete)
func attemptScaleUp(appCtx *app.Context) error {
labelSelector := appCtx.Config.Elasticsearch.Restore.ScaleDownLabelSelector
scaleUpFn := func() error {
return scale.ScaleUpAndReleaseLock(appCtx.K8sClient, appCtx.Namespace, labelSelector, appCtx.Logger)
}
if err := scaleUpFn(); err != nil {
// Don't fail if no deployments found to scale up
appCtx.Logger.Infof("No deployments found to scale up (this is normal if already finalized)")
return nil
}
appCtx.Logger.Successf("Finalization completed successfully")
return nil
}