Skip to content

Commit d1c8929

Browse files
committed
Adding cluster specific operations
1 parent 53fd1ab commit d1c8929

8 files changed

+334
-2
lines changed

.vscode/settings.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,6 @@
3232
"${commentprefix} ",
3333
""
3434
]
35-
}
35+
},
36+
"go.gopath": "${workspaceRoot}/.gobuild"
3637
}

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ SCRIPTDIR := $(shell pwd)
33
ROOTDIR := $(shell cd $(SCRIPTDIR) && pwd)
44

55
GOBUILDDIR := $(SCRIPTDIR)/.gobuild
6-
GOVERSION := 1.8.3-alpine
6+
GOVERSION := 1.9.2-alpine
77
TMPDIR := $(GOBUILDDIR)
88

99
ifndef ARANGODB

client.go

+3
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ type Client interface {
5353
// User functions
5454
ClientUsers
5555

56+
// Cluster functions
57+
ClientCluster
58+
5659
// Server/cluster administration functions
5760
ClientServerAdmin
5861
}

client_cluster.go

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Ewout Prangsma
21+
//
22+
23+
package driver
24+
25+
import "context"
26+
27+
// ClientCluster provides methods needed to access cluster functionality from a client.
28+
type ClientCluster interface {
29+
// Cluster provides access to cluster wide specific operations.
30+
// To use this interface, an ArangoDB cluster is required.
31+
// If this method is a called without a cluster, a PreconditionFailed error is returned.
32+
Cluster(ctx context.Context) (Cluster, error)
33+
}

client_cluster_impl.go

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Ewout Prangsma
21+
//
22+
23+
package driver
24+
25+
import (
26+
"context"
27+
)
28+
29+
// Cluster provides access to cluster wide specific operations.
30+
// To use this interface, an ArangoDB cluster is required.
31+
// If this method is a called without a cluster, a PreconditionFailed error is returned.
32+
func (c *client) Cluster(ctx context.Context) (Cluster, error) {
33+
role, _, err := c.role(ctx)
34+
if err != nil {
35+
return nil, WithStack(err)
36+
}
37+
if role == "SINGLE" {
38+
// Standalone server, this is wrong
39+
return nil, WithStack(newArangoError(412, 0, "Cluster expected, found SINGLE server"))
40+
}
41+
cl, err := newCluster(c.conn)
42+
if err != nil {
43+
return nil, WithStack(err)
44+
}
45+
return cl, nil
46+
}

cluster.go

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Ewout Prangsma
21+
//
22+
23+
package driver
24+
25+
import (
26+
"context"
27+
"time"
28+
)
29+
30+
// Cluster provides access to cluster wide specific operations.
31+
// To use this interface, an ArangoDB cluster is required.
32+
type Cluster interface {
33+
// Get the cluster configuration & health
34+
Health(ctx context.Context) (ClusterHealth, error)
35+
36+
// MoveShard moves a single shard of the given collection from server `fromServer` to
37+
// server `toServer`.
38+
MoveShard(ctx context.Context, col Collection, shard int, fromServer, toServer ServerID) error
39+
}
40+
41+
// ServerID identifies an arangod server in a cluster.
42+
type ServerID string
43+
44+
// ClusterHealth contains health information for all servers in a cluster.
45+
type ClusterHealth struct {
46+
// Unique identifier of the entire cluster.
47+
// This ID is created when the cluster was first created.
48+
ID string `json:"ClusterId"`
49+
// Health per server
50+
Health map[ServerID]ServerHealth `json:"Health"`
51+
}
52+
53+
// ServerHealth contains health information of a single server in a cluster.
54+
type ServerHealth struct {
55+
Endpoint string `json:"Endpoint"`
56+
LastHeartbeatAcked time.Time `json:"LastHeartbeatAcked"`
57+
LastHeartbeatSent time.Time `json:"LastHeartbeatSent"`
58+
LastHeartbeatStatus string `json:"LastHeartbeatStatus"`
59+
Role ServerRole `json:"Role"`
60+
ShortName string `json:"ShortName"`
61+
Status ServerStatus `json:"Status"`
62+
CanBeDeleted bool `json:"CanBeDeleted"`
63+
HostID string `json:"Host,omitempty"`
64+
}
65+
66+
// ServerRole is the role of an arangod server
67+
type ServerRole string
68+
69+
const (
70+
ServerRoleDBServer ServerRole = "DBServer"
71+
ServerRoleCoordinator ServerRole = "Coordinator"
72+
ServerRoleAgent ServerRole = "Agent"
73+
)
74+
75+
// ServerStatus describes the health status of a server
76+
type ServerStatus string
77+
78+
const (
79+
ServerStatusGood ServerStatus = "GOOD"
80+
)

cluster_impl.go

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Ewout Prangsma
21+
//
22+
23+
package driver
24+
25+
import "context"
26+
27+
// newCluster creates a new Cluster implementation.
28+
func newCluster(conn Connection) (Cluster, error) {
29+
if conn == nil {
30+
return nil, WithStack(InvalidArgumentError{Message: "conn is nil"})
31+
}
32+
return &cluster{
33+
conn: conn,
34+
}, nil
35+
}
36+
37+
type cluster struct {
38+
conn Connection
39+
}
40+
41+
// LoggerState returns the state of the replication logger
42+
func (c *cluster) Health(ctx context.Context) (ClusterHealth, error) {
43+
req, err := c.conn.NewRequest("GET", "_admin/cluster/health")
44+
if err != nil {
45+
return ClusterHealth{}, WithStack(err)
46+
}
47+
applyContextSettings(ctx, req)
48+
resp, err := c.conn.Do(ctx, req)
49+
if err != nil {
50+
return ClusterHealth{}, WithStack(err)
51+
}
52+
if err := resp.CheckStatus(200); err != nil {
53+
return ClusterHealth{}, WithStack(err)
54+
}
55+
var result ClusterHealth
56+
if err := resp.ParseBody("", &result); err != nil {
57+
return ClusterHealth{}, WithStack(err)
58+
}
59+
return result, nil
60+
}
61+
62+
type moveShardRequest struct {
63+
Database string `json:"database"`
64+
Collection string `json:"collection"`
65+
Shard int `json:"shard"`
66+
FromServer ServerID `json:"fromServer"`
67+
ToServer ServerID `json:"toServer"`
68+
}
69+
70+
// MoveShard moves a single shard of the given collection from server `fromServer` to
71+
// server `toServer`.
72+
func (c *cluster) MoveShard(ctx context.Context, col Collection, shard int, fromServer, toServer ServerID) error {
73+
req, err := c.conn.NewRequest("POST", "_admin/cluster/moveShard")
74+
if err != nil {
75+
return WithStack(err)
76+
}
77+
input := moveShardRequest{
78+
Database: col.Database().Name(),
79+
Collection: col.Name(),
80+
Shard: shard,
81+
FromServer: fromServer,
82+
ToServer: toServer,
83+
}
84+
if _, err := req.SetBody(input); err != nil {
85+
return WithStack(err)
86+
}
87+
applyContextSettings(ctx, req)
88+
resp, err := c.conn.Do(ctx, req)
89+
if err != nil {
90+
return WithStack(err)
91+
}
92+
if err := resp.CheckStatus(200); err != nil {
93+
return WithStack(err)
94+
}
95+
if err := resp.ParseBody("", nil); err != nil {
96+
return WithStack(err)
97+
}
98+
return nil
99+
}

test/cluster_test.go

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Ewout Prangsma
21+
//
22+
23+
package test
24+
25+
import (
26+
"context"
27+
"testing"
28+
29+
driver "github.com/arangodb/go-driver"
30+
)
31+
32+
// TestClusterHealth tests the Cluster.Health method.
33+
func TestClusterHealth(t *testing.T) {
34+
ctx := context.Background()
35+
c := createClientFromEnv(t, true)
36+
cl, err := c.Cluster(ctx)
37+
if driver.IsPreconditionFailed(err) {
38+
t.Skip("Not a cluster")
39+
} else {
40+
h, err := cl.Health(ctx)
41+
if err != nil {
42+
t.Fatalf("Health failed: %s", describe(err))
43+
}
44+
if h.ID == "" {
45+
t.Error("Expected cluster ID to be non-empty")
46+
}
47+
agents := 0
48+
dbservers := 0
49+
coordinators := 0
50+
for _, sh := range h.Health {
51+
switch sh.Role {
52+
case driver.ServerRoleAgent:
53+
agents++
54+
case driver.ServerRoleDBServer:
55+
dbservers++
56+
case driver.ServerRoleCoordinator:
57+
coordinators++
58+
}
59+
}
60+
if agents == 0 {
61+
t.Error("Expected at least 1 agent")
62+
}
63+
if dbservers == 0 {
64+
t.Error("Expected at least 1 dbserver")
65+
}
66+
if coordinators == 0 {
67+
t.Error("Expected at least 1 coordinator")
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)