Skip to content

Commit 7548d59

Browse files
committed
Adding upgrade retry
1 parent f69eadf commit 7548d59

File tree

5 files changed

+155
-12
lines changed

5 files changed

+155
-12
lines changed

client/api.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ type API interface {
5353
// StartDatabaseUpgrade is called to start the upgrade process
5454
StartDatabaseUpgrade(ctx context.Context, force bool) error
5555

56+
// RetryDatabaseUpgrade resets a failure mark in the existing upgrade plan
57+
// such that the starters will retry the upgrade once more.
58+
RetryDatabaseUpgrade(ctx context.Context) error
59+
5660
// Status returns the status of any upgrade plan
5761
UpgradeStatus(context.Context) (UpgradeStatus, error)
5862
}

client/client.go

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ package client
2525
import (
2626
"context"
2727
"encoding/json"
28-
"fmt"
2928
"io/ioutil"
3029
"net/http"
3130
"net/url"
@@ -225,6 +224,29 @@ func (c *client) StartDatabaseUpgrade(ctx context.Context, force bool) error {
225224
return nil
226225
}
227226

227+
// RetryDatabaseUpgrade resets a failure mark in the existing upgrade plan
228+
// such that the starters will retry the upgrade once more.
229+
func (c *client) RetryDatabaseUpgrade(ctx context.Context) error {
230+
url := c.createURL("/database-auto-upgrade", nil)
231+
232+
req, err := http.NewRequest("PUT", url, nil)
233+
if err != nil {
234+
return maskAny(err)
235+
}
236+
if ctx != nil {
237+
req = req.WithContext(ctx)
238+
}
239+
resp, err := c.client.Do(req)
240+
if err != nil {
241+
return maskAny(err)
242+
}
243+
if err := c.handleResponse(resp, "PUT", url, nil); err != nil {
244+
return maskAny(err)
245+
}
246+
247+
return nil
248+
}
249+
228250
// Status returns the status of any upgrade plan
229251
func (c *client) UpgradeStatus(ctx context.Context) (UpgradeStatus, error) {
230252
url := c.createURL("/database-auto-upgrade", nil)
@@ -258,11 +280,16 @@ func (c *client) handleResponse(resp *http.Response, method, url string, result
258280
}
259281

260282
if resp.StatusCode != http.StatusOK {
261-
/*var er ErrorResponse
283+
var er ErrorResponse
262284
if err := json.Unmarshal(body, &er); err == nil {
263-
return &er
264-
}*/
265-
return maskAny(fmt.Errorf("Invalid status %d", resp.StatusCode))
285+
return maskAny(StatusError{
286+
StatusCode: resp.StatusCode,
287+
message: er.Error,
288+
})
289+
}
290+
return maskAny(StatusError{
291+
StatusCode: resp.StatusCode,
292+
})
266293
}
267294

268295
// Got a success status

service/server.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,31 @@ func (s *httpServer) databaseAutoUpgradeHandler(w http.ResponseWriter, r *http.R
640640
w.Write([]byte("OK"))
641641
}
642642
}
643+
case "PUT":
644+
// Retry the upgrade process
645+
if !isRunningMaster {
646+
// We're not the starter leader.
647+
// Forward the request to the leader.
648+
c, err := createMasterClient()
649+
if err != nil {
650+
handleError(w, err)
651+
} else {
652+
if err := c.RetryDatabaseUpgrade(ctx); err != nil {
653+
handleError(w, err)
654+
} else {
655+
w.WriteHeader(http.StatusOK)
656+
w.Write([]byte("OK"))
657+
}
658+
}
659+
} else {
660+
// We're the starter leader, process the request
661+
if err := s.context.UpgradeManager().RetryDatabaseUpgrade(ctx); err != nil {
662+
handleError(w, err)
663+
} else {
664+
w.WriteHeader(http.StatusOK)
665+
w.Write([]byte("OK"))
666+
}
667+
}
643668
case "GET":
644669
if status, err := s.context.UpgradeManager().Status(ctx); err != nil {
645670
handleError(w, err)

service/upgrade_manager.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ type UpgradeManager interface {
4444
// StartDatabaseUpgrade is called to start the upgrade process
4545
StartDatabaseUpgrade(ctx context.Context, force bool) error
4646

47+
// RetryDatabaseUpgrade resets a failure mark in the existing upgrade plan
48+
// such that the starters will retry the upgrade once more.
49+
RetryDatabaseUpgrade(ctx context.Context) error
50+
4751
// Status returns the status of any upgrade plan
4852
Status(context.Context) (client.UpgradeStatus, error)
4953

@@ -124,6 +128,14 @@ func (p UpgradePlan) IsFailed() bool {
124128
return false
125129
}
126130

131+
// ResetFailures resets all Failures field to 0.
132+
func (p *UpgradePlan) ResetFailures() {
133+
for _, e := range p.Entries {
134+
e.Failures = 0
135+
e.Reason = ""
136+
}
137+
}
138+
127139
// UpgradeEntryType is a strongly typed upgrade plan item
128140
type UpgradeEntryType string
129141

@@ -352,6 +364,49 @@ func (m *upgradeManager) StartDatabaseUpgrade(ctx context.Context, force bool) e
352364
return nil
353365
}
354366

367+
// RetryDatabaseUpgrade resets a failure mark in the existing upgrade plan
368+
// such that the starters will retry the upgrade once more.
369+
func (m *upgradeManager) RetryDatabaseUpgrade(ctx context.Context) error {
370+
m.mutex.Lock()
371+
defer m.mutex.Unlock()
372+
373+
// Fetch mode
374+
_, _, mode := m.upgradeManagerContext.ClusterConfig()
375+
376+
if !mode.HasAgency() {
377+
// Without an agency there is not upgrade plan to retry
378+
return errors.Wrap(client.BadRequestError, "Retry needs an agency")
379+
}
380+
381+
// Retry upgrade with agency.
382+
plan, err := m.readUpgradePlan(ctx)
383+
if agency.IsKeyNotFound(err) {
384+
// There is no upgrade plan
385+
return errors.Wrap(client.BadRequestError, "There is no upgrade plan")
386+
}
387+
if err != nil {
388+
// Failed to read upgrade plan
389+
return errors.Wrap(err, "Failed to read upgrade plan")
390+
}
391+
392+
// Check failure status
393+
if !plan.IsFailed() {
394+
return errors.Wrap(client.BadRequestError, "The upgrade plan has not failed")
395+
}
396+
397+
// Reset failures and write plan
398+
plan.ResetFailures()
399+
overwrite := false
400+
if _, err := m.writeUpgradePlan(ctx, plan, overwrite); err != nil {
401+
return errors.Wrap(err, "Failed to write upgrade plan")
402+
}
403+
404+
// Inform user
405+
m.log.Info().Msg("Reset failures in upgrade plan so it can be retried")
406+
407+
return nil
408+
}
409+
355410
// Status returns the current status of the upgrade process.
356411
func (m *upgradeManager) Status(ctx context.Context) (client.UpgradeStatus, error) {
357412
plan, err := m.readUpgradePlan(ctx)

upgrade.go

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,31 +38,57 @@ import (
3838
var (
3939
cmdUpgrade = &cobra.Command{
4040
Use: "upgrade",
41-
Short: "Start the ArangoDB starter in the background",
41+
Short: "Upgrade an ArangoDB deployment to a new version",
4242
Run: cmdUpgradeRun,
4343
}
44+
cmdRetry = &cobra.Command{
45+
Use: "retry",
46+
Short: "Retry an operation",
47+
Run: cmdShowUsage,
48+
}
49+
cmdRetryUpgrade = &cobra.Command{
50+
Use: "upgrade",
51+
Short: "Retry a failed upgrade of an ArangoDB deployment to a new version",
52+
Run: cmdRetryUpgradeRun,
53+
}
4454
upgradeOptions struct {
4555
starterEndpoint string
4656
}
57+
retryUpgradeOptions struct {
58+
starterEndpoint string
59+
}
4760
)
4861

4962
func init() {
5063
f := cmdUpgrade.Flags()
5164
f.StringVar(&upgradeOptions.starterEndpoint, "starter.endpoint", "", "The endpoint of the starter to connect to. E.g. http://localhost:8528")
5265

66+
f = cmdRetryUpgrade.Flags()
67+
f.StringVar(&retryUpgradeOptions.starterEndpoint, "starter.endpoint", "", "The endpoint of the starter to connect to. E.g. http://localhost:8528")
68+
5369
cmdMain.AddCommand(cmdUpgrade)
70+
cmdMain.AddCommand(cmdRetry)
71+
cmdRetry.AddCommand(cmdRetryUpgrade)
5472
}
5573

5674
func cmdUpgradeRun(cmd *cobra.Command, args []string) {
75+
runUpgrade(upgradeOptions.starterEndpoint, false, false)
76+
}
77+
78+
func cmdRetryUpgradeRun(cmd *cobra.Command, args []string) {
79+
runUpgrade(retryUpgradeOptions.starterEndpoint, false, true)
80+
}
81+
82+
func runUpgrade(starterEndpoint string, force, retry bool) {
5783
// Setup logging
5884
consoleOnly := true
5985
configureLogging(consoleOnly)
6086

6187
// Check options
62-
if upgradeOptions.starterEndpoint == "" {
88+
if starterEndpoint == "" {
6389
log.Fatal().Msg("--starter.endpoint must be set")
6490
}
65-
ep, err := url.Parse(upgradeOptions.starterEndpoint)
91+
ep, err := url.Parse(starterEndpoint)
6692
if err != nil {
6793
log.Fatal().Err(err).Msg("--starter.endpoint is invalid")
6894
}
@@ -73,11 +99,17 @@ func cmdUpgradeRun(cmd *cobra.Command, args []string) {
7399
log.Fatal().Err(err).Msg("Failed to create Starter client")
74100
}
75101
ctx := context.Background()
76-
force := false
77-
if err := c.StartDatabaseUpgrade(ctx, force); err != nil {
78-
log.Fatal().Err(err).Msg("Failed to starter database automatic upgrade")
102+
if retry {
103+
if err := c.RetryDatabaseUpgrade(ctx); err != nil {
104+
log.Fatal().Err(err).Msg("Failed to retry database automatic upgrade")
105+
}
106+
log.Info().Msg("Database automatic upgrade has been restarted")
107+
} else {
108+
if err := c.StartDatabaseUpgrade(ctx, force); err != nil {
109+
log.Fatal().Err(err).Msg("Failed to start database automatic upgrade")
110+
}
111+
log.Info().Msg("Database automatic upgrade has been started")
79112
}
80-
log.Info().Msg("Database automatic upgrade has been started")
81113

82114
// Wait for the upgrade to finish
83115
remaining := ""

0 commit comments

Comments
 (0)