3030 useLatest bool
3131 runBackground bool
3232 skipConfirmation bool
33+ allowPartial bool
3334)
3435
3536func restoreCmd (globalFlags * config.CLIGlobalFlags ) * cobra.Command {
@@ -45,6 +46,7 @@ func restoreCmd(globalFlags *config.CLIGlobalFlags) *cobra.Command {
4546 cmd .Flags ().BoolVar (& useLatest , "latest" , false , "Restore from the most recent snapshot (mutually exclusive with --snapshot)" )
4647 cmd .Flags ().BoolVar (& runBackground , "background" , false , "Run restore in background without waiting for completion" )
4748 cmd .Flags ().BoolVarP (& skipConfirmation , "yes" , "y" , false , "Skip confirmation prompt" )
49+ cmd .Flags ().BoolVar (& allowPartial , "allow-partial" , false , "Allow restoring from a PARTIAL snapshot without extra confirmation" )
4850 cmd .MarkFlagsMutuallyExclusive ("snapshot" , "latest" )
4951 cmd .MarkFlagsOneRequired ("snapshot" , "latest" )
5052 return cmd
@@ -81,13 +83,25 @@ func runRestore(appCtx *app.Context) error {
8183 appCtx .Logger .Successf ("Latest snapshot found: %s" , selectedSnapshot )
8284 }
8385
86+ // Fetch snapshot details to check state
87+ snapshotDetails , err := esClient .GetSnapshot (repository , selectedSnapshot )
88+ if err != nil {
89+ return fmt .Errorf ("failed to get snapshot details: %w" , err )
90+ }
91+
92+ // Validate snapshot state
93+ if err := validateSnapshotState (snapshotDetails , appCtx , skipConfirmation , allowPartial ); err != nil {
94+ return err
95+ }
96+
8497 // Confirm with user before starting destructive operation
8598 if ! skipConfirmation {
8699 appCtx .Logger .Println ()
87100 appCtx .Logger .Warningf ("WARNING: Restoring from snapshot will DELETE all existing STS indices!" )
88101 appCtx .Logger .Warningf ("This operation cannot be undone." )
89102 appCtx .Logger .Println ()
90103 appCtx .Logger .Infof ("Snapshot to restore: %s" , selectedSnapshot )
104+ appCtx .Logger .Infof ("Snapshot state: %s" , snapshotDetails .State )
91105 appCtx .Logger .Infof ("Namespace: %s" , appCtx .Namespace )
92106 appCtx .Logger .Println ()
93107
@@ -119,8 +133,9 @@ func runRestore(appCtx *app.Context) error {
119133
120134 // Trigger async restore
121135 appCtx .Logger .Println ()
136+ isPartial := snapshotDetails .State == "PARTIAL"
122137 appCtx .Logger .Infof ("Triggering restore for snapshot: %s" , selectedSnapshot )
123- if err := esClient .RestoreSnapshot (repository , selectedSnapshot , appCtx .Config .Elasticsearch .Restore .IndicesPattern ); err != nil {
138+ if err := esClient .RestoreSnapshot (repository , selectedSnapshot , appCtx .Config .Elasticsearch .Restore .IndicesPattern , isPartial ); err != nil {
124139 return fmt .Errorf ("failed to trigger restore: %w" , err )
125140 }
126141 appCtx .Logger .Successf ("Restore triggered successfully" )
@@ -152,6 +167,39 @@ func getLatestSnapshot(esClient es.Interface, repository string) (string, error)
152167 return snapshots [0 ].Snapshot , nil
153168}
154169
170+ // validateSnapshotState checks the snapshot state and handles PARTIAL snapshots
171+ func validateSnapshotState (snapshot * es.Snapshot , appCtx * app.Context , skipConfirm , allowPartialFlag bool ) error {
172+ switch snapshot .State {
173+ case es .StatusSuccess :
174+ return nil
175+ case "PARTIAL" :
176+ failureCount := len (snapshot .Failures )
177+ if allowPartialFlag {
178+ appCtx .Logger .Warningf ("Snapshot '%s' is PARTIAL (%d shard failure(s)), proceeding due to --allow-partial flag" ,
179+ snapshot .Snapshot , failureCount )
180+ return nil
181+ }
182+ if skipConfirm {
183+ return fmt .Errorf ("snapshot '%s' is PARTIAL with %d shard failure(s); " +
184+ "use --allow-partial together with --yes to restore a partial snapshot non-interactively" ,
185+ snapshot .Snapshot , failureCount )
186+ }
187+ // Interactive mode: warn and ask for explicit confirmation
188+ appCtx .Logger .Println ()
189+ appCtx .Logger .Warningf ("WARNING: Snapshot '%s' is in PARTIAL state!" , snapshot .Snapshot )
190+ appCtx .Logger .Warningf (" %d shard(s) failed out of %d total (%d successful)" ,
191+ snapshot .Shards .Failed , snapshot .Shards .Total , snapshot .Shards .Successful )
192+ appCtx .Logger .Warningf ("Restoring this snapshot will result in incomplete data for the failed shards." )
193+ appCtx .Logger .Println ()
194+ if ! restore .PromptForConfirmation () {
195+ return fmt .Errorf ("restore operation cancelled by user" )
196+ }
197+ return nil
198+ default :
199+ return fmt .Errorf ("snapshot '%s' is in %s state and cannot be restored" , snapshot .Snapshot , snapshot .State )
200+ }
201+ }
202+
155203// deleteAllSTSIndices deletes all STS indices including datastream rollover if needed
156204func deleteAllSTSIndices (esClient es.Interface , appCtx * app.Context ) error {
157205 appCtx .Logger .Infof ("Fetching current Elasticsearch indices..." )
0 commit comments