|
|
@@ -15,15 +15,26 @@
|
|
|
package clientv3
|
|
|
|
|
|
import (
|
|
|
+ "sync"
|
|
|
+
|
|
|
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
|
|
"golang.org/x/net/context"
|
|
|
+ "google.golang.org/grpc"
|
|
|
)
|
|
|
|
|
|
type (
|
|
|
DefragmentResponse pb.DefragmentResponse
|
|
|
+ AlarmResponse pb.AlarmResponse
|
|
|
+ AlarmMember pb.AlarmMember
|
|
|
)
|
|
|
|
|
|
type Maintenance interface {
|
|
|
+ // AlarmList gets all active alarms.
|
|
|
+ AlarmList(ctx context.Context) (*AlarmResponse, error)
|
|
|
+
|
|
|
+ // AlarmDisarm disarms a given alarm.
|
|
|
+ AlarmDisarm(ctx context.Context, m *AlarmMember) (*AlarmResponse, error)
|
|
|
+
|
|
|
// Defragment defragments storage backend of the etcd member with given endpoint.
|
|
|
// Defragment is only needed when deleting a large number of keys and want to reclaim
|
|
|
// the resources.
|
|
|
@@ -36,6 +47,72 @@ type Maintenance interface {
|
|
|
|
|
|
type maintenance struct {
|
|
|
c *Client
|
|
|
+
|
|
|
+ mu sync.Mutex
|
|
|
+ conn *grpc.ClientConn // conn in-use
|
|
|
+ remote pb.MaintenanceClient
|
|
|
+}
|
|
|
+
|
|
|
+func NewMaintenance(c *Client) Maintenance {
|
|
|
+ conn := c.ActiveConnection()
|
|
|
+ return &maintenance{
|
|
|
+ c: c,
|
|
|
+ conn: conn,
|
|
|
+ remote: pb.NewMaintenanceClient(conn),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (m *maintenance) AlarmList(ctx context.Context) (*AlarmResponse, error) {
|
|
|
+ req := &pb.AlarmRequest{
|
|
|
+ Action: pb.AlarmRequest_GET,
|
|
|
+ MemberID: 0, // all
|
|
|
+ Alarm: pb.AlarmType_NONE, // all
|
|
|
+ }
|
|
|
+ for {
|
|
|
+ resp, err := m.getRemote().Alarm(ctx, req)
|
|
|
+ if err == nil {
|
|
|
+ return (*AlarmResponse)(resp), nil
|
|
|
+ }
|
|
|
+ if isHalted(ctx, err) {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if err = m.switchRemote(err); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (m *maintenance) AlarmDisarm(ctx context.Context, am *AlarmMember) (*AlarmResponse, error) {
|
|
|
+ req := &pb.AlarmRequest{
|
|
|
+ Action: pb.AlarmRequest_DEACTIVATE,
|
|
|
+ MemberID: am.MemberID,
|
|
|
+ Alarm: am.Alarm,
|
|
|
+ }
|
|
|
+
|
|
|
+ if req.MemberID == 0 && req.Alarm == pb.AlarmType_NONE {
|
|
|
+ ar, err := m.AlarmList(ctx)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ ret := AlarmResponse{}
|
|
|
+ for _, am := range ar.Alarms {
|
|
|
+ dresp, derr := m.AlarmDisarm(ctx, (*AlarmMember)(am))
|
|
|
+ if derr != nil {
|
|
|
+ return nil, derr
|
|
|
+ }
|
|
|
+ ret.Alarms = append(ret.Alarms, dresp.Alarms...)
|
|
|
+ }
|
|
|
+ return &ret, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ resp, err := m.getRemote().Alarm(ctx, req)
|
|
|
+ if err == nil {
|
|
|
+ return (*AlarmResponse)(resp), nil
|
|
|
+ }
|
|
|
+ if !isHalted(ctx, err) {
|
|
|
+ go m.switchRemote(err)
|
|
|
+ }
|
|
|
+ return nil, err
|
|
|
}
|
|
|
|
|
|
func (m *maintenance) Defragment(ctx context.Context, endpoint string) (*DefragmentResponse, error) {
|
|
|
@@ -50,3 +127,21 @@ func (m *maintenance) Defragment(ctx context.Context, endpoint string) (*Defragm
|
|
|
}
|
|
|
return (*DefragmentResponse)(resp), nil
|
|
|
}
|
|
|
+
|
|
|
+func (m *maintenance) getRemote() pb.MaintenanceClient {
|
|
|
+ m.mu.Lock()
|
|
|
+ defer m.mu.Unlock()
|
|
|
+ return m.remote
|
|
|
+}
|
|
|
+
|
|
|
+func (m *maintenance) switchRemote(prevErr error) error {
|
|
|
+ m.mu.Lock()
|
|
|
+ defer m.mu.Unlock()
|
|
|
+ newConn, err := m.c.retryConnection(m.conn, prevErr)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ m.conn = newConn
|
|
|
+ m.remote = pb.NewMaintenanceClient(m.conn)
|
|
|
+ return nil
|
|
|
+}
|