Skip to content

Commit b00fc04

Browse files
authored
Merge pull request #183 from arangodb-helper/feature/upgrade-plan
Upgrade plan
2 parents a947fdd + e0252c3 commit b00fc04

23 files changed

+2257
-297
lines changed

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ ifdef TRAVIS
5858
echo Using IP=$(IP)
5959
endif
6060

61-
TEST_TIMEOUT := 20m
61+
TEST_TIMEOUT := 25m
6262

6363
BINNAME := arangodb$(GOEXE)
6464
BIN := $(BINDIR)/$(GOOS)/$(GOARCH)/$(BINNAME)
@@ -125,6 +125,7 @@ $(GOBUILDDIR):
125125
@rm -f $(GOBUILDDIR)/src/github.com/ryanuber && ln -s ../../../deps/github.com/ryanuber $(GOBUILDDIR)/src/github.com/ryanuber
126126
@rm -f $(GOBUILDDIR)/src/github.com/voxelbrain && ln -s ../../../deps/github.com/voxelbrain $(GOBUILDDIR)/src/github.com/voxelbrain
127127
@rm -f $(GOBUILDDIR)/src/golang.org/x && ln -s ../../../deps/golang.org/x $(GOBUILDDIR)/src/golang.org/x
128+
@GOPATH=$(GOBUILDDIR) go get github.com/arangodb/go-upgrade-rules
128129

129130
.PHONY: $(CACHEVOL)
130131
$(CACHEVOL):

client/api.go

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@
2222

2323
package client
2424

25-
import "context"
25+
import (
26+
"context"
27+
28+
driver "github.com/arangodb/go-driver"
29+
)
2630

2731
// API is the interface implemented by the starter's HTTP API's.
2832
type API interface {
@@ -32,6 +36,10 @@ type API interface {
3236
// Version requests the starter version.
3337
Version(ctx context.Context) (VersionInfo, error)
3438

39+
// DatabaseVersion returns the version of the `arangod` binary that is being
40+
// used by this starter.
41+
DatabaseVersion(ctx context.Context) (driver.Version, error)
42+
3543
// Processes loads information of all the database server processes launched by the starter.
3644
Processes(ctx context.Context) (ProcessList, error)
3745

@@ -41,6 +49,22 @@ type API interface {
4149
// Shutdown will shutdown a starter (and all its started database servers).
4250
// With goodbye set, it will remove the peer slot for the starter.
4351
Shutdown(ctx context.Context, goodbye bool) error
52+
53+
// StartDatabaseUpgrade is called to start the upgrade process
54+
StartDatabaseUpgrade(ctx context.Context) error
55+
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+
60+
// AbortDatabaseUpgrade removes the existing upgrade plan.
61+
// Note that Starters working on an entry of the upgrade
62+
// will finish that entry.
63+
// If there is no plan, a NotFoundError will be returned.
64+
AbortDatabaseUpgrade(ctx context.Context) error
65+
66+
// Status returns the status of any upgrade plan
67+
UpgradeStatus(context.Context) (UpgradeStatus, error)
4468
}
4569

4670
// IDInfo contains the ID of the starter
@@ -54,6 +78,11 @@ type VersionInfo struct {
5478
Build string `json:"build"`
5579
}
5680

81+
// DatabaseVersionResponse is the JSON response of a `/database-version` request.
82+
type DatabaseVersionResponse struct {
83+
Version driver.Version `json:"version"`
84+
}
85+
5786
// EndpointList is the JSON response of a `/endpoints` request.
5887
// It contains URL's of all starters, agents & coordinators in the cluster.
5988
type EndpointList struct {
@@ -101,3 +130,33 @@ func (list ProcessList) ServerByType(serverType ServerType) (ServerProcess, bool
101130
}
102131
return ServerProcess{}, false
103132
}
133+
134+
// UpgradeStatus is the JSON structure returns from a `GET /database-auto-upgrade`
135+
// request.
136+
type UpgradeStatus struct {
137+
// Ready is set to true when the entire upgrade has been finished succesfully.
138+
Ready bool `json:"ready"`
139+
// Failed is set to true when the upgrade process has yielded an error
140+
Failed bool `json:"failed"`
141+
// Reasons contains a human readable description of the state
142+
Reason string `json:"reason,omitempty"`
143+
// FromVersions contains all database versions found that will be upgraded.
144+
FromVersions []driver.Version `json:"from_versions"`
145+
// ToVersion contains the database version that will be upgraded to.
146+
ToVersion driver.Version `json:"to_version"`
147+
// ServersUpgraded contains the servers that have been upgraded
148+
ServersUpgraded []UpgradeStatusServer `json:"servers_upgraded"`
149+
// ServersRemaining contains the servers that have not yet been upgraded
150+
ServersRemaining []UpgradeStatusServer `json:"servers_remaining"`
151+
}
152+
153+
// UpgradeStatusServer is the nested JSON structure returns from a `GET /database-auto-upgrade`
154+
// request.
155+
type UpgradeStatusServer struct {
156+
// Type of the server
157+
Type ServerType `json:"type"`
158+
// Port the server is listening on
159+
Port int `json:"port"`
160+
// Address of the server (IP or hostname)
161+
Address string `json:"address"`
162+
}

client/client.go

Lines changed: 127 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ package client
2525
import (
2626
"context"
2727
"encoding/json"
28-
"fmt"
2928
"io/ioutil"
3029
"net/http"
3130
"net/url"
3231

32+
driver "github.com/arangodb/go-driver"
3333
"github.com/pkg/errors"
3434
)
3535

@@ -101,6 +101,30 @@ func (c *client) Version(ctx context.Context) (VersionInfo, error) {
101101
return result, nil
102102
}
103103

104+
// DatabaseVersion returns the version of the `arangod` binary that is being
105+
// used by this starter.
106+
func (c *client) DatabaseVersion(ctx context.Context) (driver.Version, error) {
107+
url := c.createURL("/database-version", nil)
108+
109+
var result DatabaseVersionResponse
110+
req, err := http.NewRequest("GET", url, nil)
111+
if err != nil {
112+
return "", maskAny(err)
113+
}
114+
if ctx != nil {
115+
req = req.WithContext(ctx)
116+
}
117+
resp, err := c.client.Do(req)
118+
if err != nil {
119+
return "", maskAny(err)
120+
}
121+
if err := c.handleResponse(resp, "GET", url, &result); err != nil {
122+
return "", maskAny(err)
123+
}
124+
125+
return result.Version, nil
126+
}
127+
104128
// Processes loads information of all the server processes launched by a specific arangodb.
105129
func (c *client) Processes(ctx context.Context) (ProcessList, error) {
106130
url := c.createURL("/process", nil)
@@ -174,6 +198,99 @@ func (c *client) Shutdown(ctx context.Context, goodbye bool) error {
174198
return nil
175199
}
176200

201+
// StartDatabaseUpgrade is called to start the upgrade process
202+
func (c *client) StartDatabaseUpgrade(ctx context.Context) error {
203+
url := c.createURL("/database-auto-upgrade", nil)
204+
205+
req, err := http.NewRequest("POST", url, nil)
206+
if err != nil {
207+
return maskAny(err)
208+
}
209+
if ctx != nil {
210+
req = req.WithContext(ctx)
211+
}
212+
resp, err := c.client.Do(req)
213+
if err != nil {
214+
return maskAny(err)
215+
}
216+
if err := c.handleResponse(resp, "POST", url, nil); err != nil {
217+
return maskAny(err)
218+
}
219+
220+
return nil
221+
}
222+
223+
// RetryDatabaseUpgrade resets a failure mark in the existing upgrade plan
224+
// such that the starters will retry the upgrade once more.
225+
func (c *client) RetryDatabaseUpgrade(ctx context.Context) error {
226+
url := c.createURL("/database-auto-upgrade", nil)
227+
228+
req, err := http.NewRequest("PUT", url, nil)
229+
if err != nil {
230+
return maskAny(err)
231+
}
232+
if ctx != nil {
233+
req = req.WithContext(ctx)
234+
}
235+
resp, err := c.client.Do(req)
236+
if err != nil {
237+
return maskAny(err)
238+
}
239+
if err := c.handleResponse(resp, "PUT", url, nil); err != nil {
240+
return maskAny(err)
241+
}
242+
243+
return nil
244+
}
245+
246+
// AbortDatabaseUpgrade removes the existing upgrade plan.
247+
// Note that Starters working on an entry of the upgrade
248+
// will finish that entry.
249+
// If there is no plan, a NotFoundError will be returned.
250+
func (c *client) AbortDatabaseUpgrade(ctx context.Context) error {
251+
url := c.createURL("/database-auto-upgrade", nil)
252+
253+
req, err := http.NewRequest("DELETE", url, nil)
254+
if err != nil {
255+
return maskAny(err)
256+
}
257+
if ctx != nil {
258+
req = req.WithContext(ctx)
259+
}
260+
resp, err := c.client.Do(req)
261+
if err != nil {
262+
return maskAny(err)
263+
}
264+
if err := c.handleResponse(resp, "DELETE", url, nil); err != nil {
265+
return maskAny(err)
266+
}
267+
268+
return nil
269+
}
270+
271+
// Status returns the status of any upgrade plan
272+
func (c *client) UpgradeStatus(ctx context.Context) (UpgradeStatus, error) {
273+
url := c.createURL("/database-auto-upgrade", nil)
274+
275+
var result UpgradeStatus
276+
req, err := http.NewRequest("GET", url, nil)
277+
if err != nil {
278+
return UpgradeStatus{}, maskAny(err)
279+
}
280+
if ctx != nil {
281+
req = req.WithContext(ctx)
282+
}
283+
resp, err := c.client.Do(req)
284+
if err != nil {
285+
return UpgradeStatus{}, maskAny(err)
286+
}
287+
if err := c.handleResponse(resp, "GET", url, &result); err != nil {
288+
return UpgradeStatus{}, maskAny(err)
289+
}
290+
291+
return result, nil
292+
}
293+
177294
// handleResponse checks the given response status and decodes any JSON result.
178295
func (c *client) handleResponse(resp *http.Response, method, url string, result interface{}) error {
179296
// Read response body into memory
@@ -184,11 +301,16 @@ func (c *client) handleResponse(resp *http.Response, method, url string, result
184301
}
185302

186303
if resp.StatusCode != http.StatusOK {
187-
/*var er ErrorResponse
304+
var er ErrorResponse
188305
if err := json.Unmarshal(body, &er); err == nil {
189-
return &er
190-
}*/
191-
return maskAny(fmt.Errorf("Invalid status %d", resp.StatusCode))
306+
return maskAny(StatusError{
307+
StatusCode: resp.StatusCode,
308+
message: er.Error,
309+
})
310+
}
311+
return maskAny(StatusError{
312+
StatusCode: resp.StatusCode,
313+
})
192314
}
193315

194316
// Got a success status

client/error.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ var (
4343
InternalServerError = StatusError{StatusCode: http.StatusInternalServerError, message: "internal server error"}
4444
)
4545

46+
// StatusError is an error with a given HTTP status code.
4647
type StatusError struct {
4748
StatusCode int
4849
message string
@@ -75,10 +76,16 @@ func IsStatusErrorWithCode(err error, code int) bool {
7576
return false
7677
}
7778

79+
// ErrorResponse is the JSON structure returned in an API error.
7880
type ErrorResponse struct {
7981
Error string
8082
}
8183

84+
// IsNotFound returns true if the given error is caused by a NotFoundError.
85+
func IsNotFound(err error) bool {
86+
return IsStatusErrorWithCode(err, http.StatusNotFound)
87+
}
88+
8289
// IsServiceUnavailable returns true if the given error is caused by a ServiceUnavailableError.
8390
func IsServiceUnavailable(err error) bool {
8491
return IsStatusErrorWithCode(err, http.StatusServiceUnavailable)
@@ -99,6 +106,31 @@ func IsInternalServer(err error) bool {
99106
return IsStatusErrorWithCode(err, http.StatusInternalServerError)
100107
}
101108

109+
// NewNotFoundError creates a not found error with given message.
110+
func NewNotFoundError(msg string) error {
111+
return StatusError{StatusCode: http.StatusNotFound, message: msg}
112+
}
113+
114+
// NewServiceUnavailableError creates a service unavailable error with given message.
115+
func NewServiceUnavailableError(msg string) error {
116+
return StatusError{StatusCode: http.StatusServiceUnavailable, message: msg}
117+
}
118+
119+
// NewBadRequestError creates a bad request error with given message.
120+
func NewBadRequestError(msg string) error {
121+
return StatusError{StatusCode: http.StatusBadRequest, message: msg}
122+
}
123+
124+
// NewPreconditionFailedError creates a precondition failed error with given message.
125+
func NewPreconditionFailedError(msg string) error {
126+
return StatusError{StatusCode: http.StatusPreconditionFailed, message: msg}
127+
}
128+
129+
// NewInternalServerError creates a internal server error with given message.
130+
func NewInternalServerError(msg string) error {
131+
return StatusError{StatusCode: http.StatusInternalServerError, message: msg}
132+
}
133+
102134
// ParseResponseError returns an error from given response.
103135
// It tries to parse the body (if given body is nil, will be read from response)
104136
// for ErrorResponse.

0 commit comments

Comments
 (0)