go.etcd.io/etcd@v3.3.27+incompatible/etcdserver/api/v3rpc/maintenance.go (about)

     1  // Copyright 2016 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package v3rpc
    16  
    17  import (
    18  	"context"
    19  	"crypto/sha256"
    20  	"io"
    21  	"time"
    22  
    23  	"github.com/coreos/etcd/auth"
    24  	"github.com/coreos/etcd/etcdserver"
    25  	"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
    26  	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
    27  	"github.com/coreos/etcd/mvcc"
    28  	"github.com/coreos/etcd/mvcc/backend"
    29  	"github.com/coreos/etcd/pkg/types"
    30  	"github.com/coreos/etcd/version"
    31  	"github.com/dustin/go-humanize"
    32  )
    33  
    34  type KVGetter interface {
    35  	KV() mvcc.ConsistentWatchableKV
    36  }
    37  
    38  type BackendGetter interface {
    39  	Backend() backend.Backend
    40  }
    41  
    42  type Alarmer interface {
    43  	Alarm(ctx context.Context, ar *pb.AlarmRequest) (*pb.AlarmResponse, error)
    44  }
    45  
    46  type LeaderTransferrer interface {
    47  	MoveLeader(ctx context.Context, lead, target uint64) error
    48  }
    49  
    50  type RaftStatusGetter interface {
    51  	etcdserver.RaftTimer
    52  	ID() types.ID
    53  	Leader() types.ID
    54  }
    55  
    56  type AuthGetter interface {
    57  	AuthInfoFromCtx(ctx context.Context) (*auth.AuthInfo, error)
    58  	AuthStore() auth.AuthStore
    59  }
    60  
    61  type maintenanceServer struct {
    62  	rg  RaftStatusGetter
    63  	kg  KVGetter
    64  	bg  BackendGetter
    65  	a   Alarmer
    66  	lt  LeaderTransferrer
    67  	hdr header
    68  }
    69  
    70  func NewMaintenanceServer(s *etcdserver.EtcdServer) pb.MaintenanceServer {
    71  	srv := &maintenanceServer{rg: s, kg: s, bg: s, a: s, lt: s, hdr: newHeader(s)}
    72  	return &authMaintenanceServer{srv, s}
    73  }
    74  
    75  func (ms *maintenanceServer) Defragment(ctx context.Context, sr *pb.DefragmentRequest) (*pb.DefragmentResponse, error) {
    76  	plog.Noticef("starting to defragment the storage backend...")
    77  	err := ms.bg.Backend().Defrag()
    78  	if err != nil {
    79  		plog.Errorf("failed to defragment the storage backend (%v)", err)
    80  		return nil, err
    81  	}
    82  	plog.Noticef("finished defragmenting the storage backend")
    83  	return &pb.DefragmentResponse{}, nil
    84  }
    85  
    86  // big enough size to hold >1 OS pages in the buffer
    87  const snapshotSendBufferSize = 32 * 1024
    88  
    89  func (ms *maintenanceServer) Snapshot(sr *pb.SnapshotRequest, srv pb.Maintenance_SnapshotServer) error {
    90  	snap := ms.bg.Backend().Snapshot()
    91  	pr, pw := io.Pipe()
    92  
    93  	defer pr.Close()
    94  
    95  	go func() {
    96  		snap.WriteTo(pw)
    97  		if err := snap.Close(); err != nil {
    98  			plog.Errorf("error closing snapshot (%v)", err)
    99  		}
   100  		pw.Close()
   101  	}()
   102  
   103  	// record SHA digest of snapshot data
   104  	// used for integrity checks during snapshot restore operation
   105  	h := sha256.New()
   106  
   107  	// buffer just holds read bytes from stream
   108  	// response size is multiple of OS page size, fetched in boltdb
   109  	// e.g. 4*1024
   110  	buf := make([]byte, snapshotSendBufferSize)
   111  
   112  	sent := int64(0)
   113  	total := snap.Size()
   114  	size := humanize.Bytes(uint64(total))
   115  
   116  	start := time.Now()
   117  	plog.Infof("sending database snapshot to client %s [%d bytes]", size, total)
   118  	for total-sent > 0 {
   119  		n, err := io.ReadFull(pr, buf)
   120  		if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
   121  			return togRPCError(err)
   122  		}
   123  		sent += int64(n)
   124  
   125  		// if total is x * snapshotSendBufferSize. it is possible that
   126  		// resp.RemainingBytes == 0
   127  		// resp.Blob == zero byte but not nil
   128  		// does this make server response sent to client nil in proto
   129  		// and client stops receiving from snapshot stream before
   130  		// server sends snapshot SHA?
   131  		// No, the client will still receive non-nil response
   132  		// until server closes the stream with EOF
   133  
   134  		resp := &pb.SnapshotResponse{
   135  			RemainingBytes: uint64(total - sent),
   136  			Blob:           buf[:n],
   137  		}
   138  		if err = srv.Send(resp); err != nil {
   139  			return togRPCError(err)
   140  		}
   141  		h.Write(buf[:n])
   142  	}
   143  
   144  	// send SHA digest for integrity checks
   145  	// during snapshot restore operation
   146  	sha := h.Sum(nil)
   147  
   148  	plog.Infof("sending database sha256 checksum to client [%d bytes]", len(sha))
   149  	hresp := &pb.SnapshotResponse{RemainingBytes: 0, Blob: sha}
   150  	if err := srv.Send(hresp); err != nil {
   151  		return togRPCError(err)
   152  	}
   153  
   154  	plog.Infof("successfully sent database snapshot to client %s [%d bytes, took %s]", size, total, humanize.Time(start))
   155  	return nil
   156  }
   157  
   158  func (ms *maintenanceServer) Hash(ctx context.Context, r *pb.HashRequest) (*pb.HashResponse, error) {
   159  	h, rev, err := ms.kg.KV().Hash()
   160  	if err != nil {
   161  		return nil, togRPCError(err)
   162  	}
   163  	resp := &pb.HashResponse{Header: &pb.ResponseHeader{Revision: rev}, Hash: h}
   164  	ms.hdr.fill(resp.Header)
   165  	return resp, nil
   166  }
   167  
   168  func (ms *maintenanceServer) HashKV(ctx context.Context, r *pb.HashKVRequest) (*pb.HashKVResponse, error) {
   169  	h, rev, compactRev, err := ms.kg.KV().HashByRev(r.Revision)
   170  	if err != nil {
   171  		return nil, togRPCError(err)
   172  	}
   173  
   174  	resp := &pb.HashKVResponse{Header: &pb.ResponseHeader{Revision: rev}, Hash: h, CompactRevision: compactRev}
   175  	ms.hdr.fill(resp.Header)
   176  	return resp, nil
   177  }
   178  
   179  func (ms *maintenanceServer) Alarm(ctx context.Context, ar *pb.AlarmRequest) (*pb.AlarmResponse, error) {
   180  	return ms.a.Alarm(ctx, ar)
   181  }
   182  
   183  func (ms *maintenanceServer) Status(ctx context.Context, ar *pb.StatusRequest) (*pb.StatusResponse, error) {
   184  	resp := &pb.StatusResponse{
   185  		Header:    &pb.ResponseHeader{Revision: ms.hdr.rev()},
   186  		Version:   version.Version,
   187  		DbSize:    ms.bg.Backend().Size(),
   188  		Leader:    uint64(ms.rg.Leader()),
   189  		RaftIndex: ms.rg.Index(),
   190  		RaftTerm:  ms.rg.Term(),
   191  	}
   192  	ms.hdr.fill(resp.Header)
   193  	return resp, nil
   194  }
   195  
   196  func (ms *maintenanceServer) MoveLeader(ctx context.Context, tr *pb.MoveLeaderRequest) (*pb.MoveLeaderResponse, error) {
   197  	if ms.rg.ID() != ms.rg.Leader() {
   198  		return nil, rpctypes.ErrGRPCNotLeader
   199  	}
   200  
   201  	if err := ms.lt.MoveLeader(ctx, uint64(ms.rg.Leader()), tr.TargetID); err != nil {
   202  		return nil, togRPCError(err)
   203  	}
   204  	return &pb.MoveLeaderResponse{}, nil
   205  }
   206  
   207  type authMaintenanceServer struct {
   208  	*maintenanceServer
   209  	ag AuthGetter
   210  }
   211  
   212  func (ams *authMaintenanceServer) isAuthenticated(ctx context.Context) error {
   213  	authInfo, err := ams.ag.AuthInfoFromCtx(ctx)
   214  	if err != nil {
   215  		return err
   216  	}
   217  
   218  	return ams.ag.AuthStore().IsAdminPermitted(authInfo)
   219  }
   220  
   221  func (ams *authMaintenanceServer) Defragment(ctx context.Context, sr *pb.DefragmentRequest) (*pb.DefragmentResponse, error) {
   222  	if err := ams.isAuthenticated(ctx); err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	return ams.maintenanceServer.Defragment(ctx, sr)
   227  }
   228  
   229  func (ams *authMaintenanceServer) Snapshot(sr *pb.SnapshotRequest, srv pb.Maintenance_SnapshotServer) error {
   230  	if err := ams.isAuthenticated(srv.Context()); err != nil {
   231  		return err
   232  	}
   233  
   234  	return ams.maintenanceServer.Snapshot(sr, srv)
   235  }
   236  
   237  func (ams *authMaintenanceServer) Hash(ctx context.Context, r *pb.HashRequest) (*pb.HashResponse, error) {
   238  	if err := ams.isAuthenticated(ctx); err != nil {
   239  		return nil, err
   240  	}
   241  
   242  	return ams.maintenanceServer.Hash(ctx, r)
   243  }
   244  
   245  func (ams *authMaintenanceServer) HashKV(ctx context.Context, r *pb.HashKVRequest) (*pb.HashKVResponse, error) {
   246  	if err := ams.isAuthenticated(ctx); err != nil {
   247  		return nil, err
   248  	}
   249  	return ams.maintenanceServer.HashKV(ctx, r)
   250  }
   251  
   252  func (ams *authMaintenanceServer) Status(ctx context.Context, ar *pb.StatusRequest) (*pb.StatusResponse, error) {
   253  	return ams.maintenanceServer.Status(ctx, ar)
   254  }
   255  
   256  func (ams *authMaintenanceServer) MoveLeader(ctx context.Context, tr *pb.MoveLeaderRequest) (*pb.MoveLeaderResponse, error) {
   257  	return ams.maintenanceServer.MoveLeader(ctx, tr)
   258  }