@@ -28,13 +28,21 @@ import (
2828 "github.com/arangodb/go-driver"
2929 "github.com/rs/zerolog"
3030
31+ backupApi "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1"
3132 api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
33+ "github.com/arangodb/kube-arangodb/pkg/util/arangod/conn"
34+ "github.com/arangodb/kube-arangodb/pkg/util/errors"
3235)
3336
3437func init () {
3538 registerAction (api .ActionTypeBackupRestore , newBackupRestoreAction , backupRestoreTimeout )
3639}
3740
41+ const (
42+ actionBackupRestoreLocalJobID api.PlanLocalKey = "jobID"
43+ actionBackupRestoreLocalBackupName api.PlanLocalKey = "backupName"
44+ )
45+
3846func newBackupRestoreAction (log zerolog.Logger , action api.Action , actionCtx ActionContext ) Action {
3947 a := & actionBackupRestore {}
4048
@@ -47,8 +55,6 @@ func newBackupRestoreAction(log zerolog.Logger, action api.Action, actionCtx Act
4755type actionBackupRestore struct {
4856 // actionImpl implement timeout and member id functions
4957 actionImpl
50-
51- actionEmptyCheckProgress
5258}
5359
5460func (a actionBackupRestore ) Start (ctx context.Context ) (bool , error ) {
@@ -64,13 +70,6 @@ func (a actionBackupRestore) Start(ctx context.Context) (bool, error) {
6470 return true , nil
6571 }
6672
67- ctxChild , cancel := globals .GetGlobalTimeouts ().ArangoD ().WithTimeout (ctx )
68- defer cancel ()
69- dbc , err := a .actionCtx .GetDatabaseClient (ctxChild )
70- if err != nil {
71- return false , err
72- }
73-
7473 backupResource , err := a .actionCtx .GetBackup (ctx , * spec .RestoreFrom )
7574 if err != nil {
7675 a .log .Error ().Err (err ).Msg ("Unable to find backup" )
@@ -96,15 +95,61 @@ func (a actionBackupRestore) Start(ctx context.Context) (bool, error) {
9695 return false , err
9796 }
9897
98+ switch mode := a .actionCtx .GetSpec ().Mode .Get (); mode {
99+ case api .DeploymentModeActiveFailover , api .DeploymentModeSingle :
100+ return a .restoreSync (ctx , backupResource )
101+ case api .DeploymentModeCluster :
102+ return a .restoreAsync (ctx , backupResource )
103+ default :
104+ return false , errors .Newf ("Unknown mode %s" , mode )
105+ }
106+ }
107+
108+ func (a actionBackupRestore ) restoreAsync (ctx context.Context , backup * backupApi.ArangoBackup ) (bool , error ) {
109+ ctxChild , cancel := globals .GetGlobalTimeouts ().ArangoD ().WithTimeout (ctx )
110+ defer cancel ()
111+
112+ dbc , err := a .actionCtx .GetDatabaseAsyncClient (ctxChild )
113+ if err != nil {
114+ return false , errors .Wrapf (err , "Unable to create client" )
115+ }
116+
117+ ctxChild , cancel = globals .GetGlobalTimeouts ().ArangoD ().WithTimeout (ctx )
118+ defer cancel ()
119+
120+ if err := dbc .Backup ().Restore (ctxChild , driver .BackupID (backup .Status .Backup .ID ), nil ); err != nil {
121+ if id , ok := conn .IsAsyncJobInProgress (err ); ok {
122+ a .actionCtx .Add (actionBackupRestoreLocalJobID , id , true )
123+ a .actionCtx .Add (actionBackupRestoreLocalBackupName , backup .GetName (), true )
124+
125+ // Async request has been send
126+ return false , nil
127+ } else {
128+ return false , errors .Wrapf (err , "Unknown restore error" )
129+ }
130+ }
131+
132+ return false , errors .Newf ("Async response not received" )
133+ }
134+
135+ func (a actionBackupRestore ) restoreSync (ctx context.Context , backup * backupApi.ArangoBackup ) (bool , error ) {
136+ ctxChild , cancel := globals .GetGlobalTimeouts ().ArangoD ().WithTimeout (ctx )
137+ defer cancel ()
138+ dbc , err := a .actionCtx .GetDatabaseClient (ctxChild )
139+ if err != nil {
140+ a .log .Debug ().Err (err ).Msg ("Failed to create database client" )
141+ return false , nil
142+ }
143+
99144 // The below action can take a while so the full parent timeout context is used.
100- restoreError := dbc .Backup ().Restore (ctx , driver .BackupID (backupResource .Status .Backup .ID ), nil )
145+ restoreError := dbc .Backup ().Restore (ctx , driver .BackupID (backup .Status .Backup .ID ), nil )
101146 if restoreError != nil {
102147 a .log .Error ().Err (restoreError ).Msg ("Restore failed" )
103148 }
104149
105150 if err := a .actionCtx .WithStatusUpdate (ctx , func (s * api.DeploymentStatus ) bool {
106151 result := & api.DeploymentRestoreResult {
107- RequestedFrom : spec . GetRestoreFrom (),
152+ RequestedFrom : backup . GetName (),
108153 }
109154
110155 if restoreError != nil {
@@ -118,9 +163,70 @@ func (a actionBackupRestore) Start(ctx context.Context) (bool, error) {
118163
119164 return true
120165 }); err != nil {
121- a .log .Error ().Err (err ).Msg ("Unable to ser restored state" )
166+ a .log .Error ().Err (err ).Msg ("Unable to set restored state" )
122167 return false , err
123168 }
124169
125170 return true , nil
126171}
172+
173+ func (a actionBackupRestore ) CheckProgress (ctx context.Context ) (bool , bool , error ) {
174+ backup , ok := a .actionCtx .Get (a .action , actionBackupRestoreLocalBackupName )
175+ if ! ok {
176+ return false , false , errors .Newf ("Local Key is missing in action: %s" , actionBackupRestoreLocalBackupName )
177+ }
178+
179+ job , ok := a .actionCtx .Get (a .action , actionBackupRestoreLocalJobID )
180+ if ! ok {
181+ return false , false , errors .Newf ("Local Key is missing in action: %s" , actionBackupRestoreLocalJobID )
182+ }
183+
184+ ctxChild , cancel := globals .GetGlobalTimeouts ().ArangoD ().WithTimeout (ctx )
185+ defer cancel ()
186+
187+ dbc , err := a .actionCtx .GetDatabaseAsyncClient (ctxChild )
188+ if err != nil {
189+ a .log .Debug ().Err (err ).Msg ("Failed to create database client" )
190+ return false , false , nil
191+ }
192+
193+ ctxChild , cancel = globals .GetGlobalTimeouts ().ArangoD ().WithTimeout (ctx )
194+ defer cancel ()
195+
196+ // Params does not matter in async fetch
197+ restoreError := dbc .Backup ().Restore (conn .WithAsyncID (ctxChild , job ), "" , nil )
198+ if restoreError != nil {
199+ if _ , ok := conn .IsAsyncJobInProgress (restoreError ); ok {
200+ // Job still in progress
201+ return false , false , nil
202+ }
203+
204+ if errors .IsTemporary (restoreError ) {
205+ // Retry
206+ return false , false , nil
207+ }
208+ }
209+
210+ // Restore is done
211+
212+ if err := a .actionCtx .WithStatusUpdate (ctx , func (s * api.DeploymentStatus ) bool {
213+ result := & api.DeploymentRestoreResult {
214+ RequestedFrom : backup ,
215+ State : api .DeploymentRestoreStateRestored ,
216+ }
217+
218+ if restoreError != nil {
219+ result .State = api .DeploymentRestoreStateRestoreFailed
220+ result .Message = restoreError .Error ()
221+ }
222+
223+ s .Restore = result
224+
225+ return true
226+ }); err != nil {
227+ a .log .Error ().Err (err ).Msg ("Unable to set restored state" )
228+ return false , false , err
229+ }
230+
231+ return true , false , nil
232+ }
0 commit comments