github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/remotesrv/grpc.go (about) 1 // Copyright 2019 Dolthub, Inc. 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 remotesrv 16 17 import ( 18 "context" 19 "encoding/base64" 20 "errors" 21 "fmt" 22 "io" 23 "net/url" 24 "path/filepath" 25 "strconv" 26 "strings" 27 "sync/atomic" 28 29 "github.com/sirupsen/logrus" 30 "google.golang.org/grpc/codes" 31 "google.golang.org/grpc/metadata" 32 "google.golang.org/grpc/status" 33 34 remotesapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/remotesapi/v1alpha1" 35 "github.com/dolthub/dolt/go/libraries/doltcore/remotestorage" 36 "github.com/dolthub/dolt/go/libraries/utils/filesys" 37 "github.com/dolthub/dolt/go/store/chunks" 38 "github.com/dolthub/dolt/go/store/hash" 39 "github.com/dolthub/dolt/go/store/types" 40 ) 41 42 var ErrUnimplemented = errors.New("unimplemented") 43 44 const RepoPathField = "repo_path" 45 46 type RemoteChunkStore struct { 47 HttpHost string 48 httpScheme string 49 50 concurrencyControl remotesapi.PushConcurrencyControl 51 52 csCache DBCache 53 bucket string 54 fs filesys.Filesys 55 lgr *logrus.Entry 56 sealer Sealer 57 remotesapi.UnimplementedChunkStoreServiceServer 58 } 59 60 func NewHttpFSBackedChunkStore(lgr *logrus.Entry, httpHost string, csCache DBCache, fs filesys.Filesys, scheme string, concurrencyControl remotesapi.PushConcurrencyControl, sealer Sealer) *RemoteChunkStore { 61 if concurrencyControl == remotesapi.PushConcurrencyControl_PUSH_CONCURRENCY_CONTROL_UNSPECIFIED { 62 concurrencyControl = remotesapi.PushConcurrencyControl_PUSH_CONCURRENCY_CONTROL_IGNORE_WORKING_SET 63 } 64 return &RemoteChunkStore{ 65 HttpHost: httpHost, 66 httpScheme: scheme, 67 concurrencyControl: concurrencyControl, 68 csCache: csCache, 69 bucket: "", 70 fs: fs, 71 lgr: lgr.WithFields(logrus.Fields{ 72 "service": "dolt.services.remotesapi.v1alpha1.ChunkStoreServiceServer", 73 }), 74 sealer: sealer, 75 } 76 } 77 78 type repoRequest interface { 79 GetRepoId() *remotesapi.RepoId 80 GetRepoPath() string 81 } 82 83 func getRepoPath(req repoRequest) string { 84 if req.GetRepoPath() != "" { 85 return req.GetRepoPath() 86 } 87 if repoId := req.GetRepoId(); repoId != nil { 88 return repoId.Org + "/" + repoId.RepoName 89 } 90 panic("unexpected empty repo_path and nil repo_id") 91 } 92 93 func (rs *RemoteChunkStore) HasChunks(ctx context.Context, req *remotesapi.HasChunksRequest) (*remotesapi.HasChunksResponse, error) { 94 logger := getReqLogger(rs.lgr, "HasChunks") 95 if err := ValidateHasChunksRequest(req); err != nil { 96 return nil, status.Error(codes.InvalidArgument, err.Error()) 97 } 98 repoPath := getRepoPath(req) 99 logger = logger.WithField(RepoPathField, repoPath) 100 defer func() { logger.Info("finished") }() 101 102 cs, err := rs.getStore(ctx, logger, repoPath) 103 if err != nil { 104 return nil, err 105 } 106 107 hashes, hashToIndex := remotestorage.ParseByteSlices(req.Hashes) 108 109 absent, err := cs.HasMany(ctx, hashes) 110 if err != nil { 111 logger.WithError(err).Error("error calling HasMany") 112 return nil, status.Error(codes.Internal, "HasMany failure:"+err.Error()) 113 } 114 115 indices := make([]int32, len(absent)) 116 117 n := 0 118 for h := range absent { 119 indices[n] = int32(hashToIndex[h]) 120 n++ 121 } 122 123 resp := &remotesapi.HasChunksResponse{ 124 Absent: indices, 125 } 126 127 logger = logger.WithFields(logrus.Fields{ 128 "num_requested": len(hashToIndex), 129 "num_absent": len(indices), 130 }) 131 132 return resp, nil 133 } 134 135 func (rs *RemoteChunkStore) getRelativeStorePath(cs RemoteSrvStore) (string, error) { 136 cspath, ok := cs.Path() 137 if !ok { 138 return "", status.Error(codes.Internal, "chunkstore misconfigured; cannot generate HTTP paths") 139 } 140 httproot, err := rs.fs.Abs(".") 141 if err != nil { 142 return "", err 143 } 144 prefix, err := filepath.Rel(httproot, cspath) 145 if err != nil { 146 return "", err 147 } 148 return prefix, nil 149 } 150 151 func (rs *RemoteChunkStore) GetDownloadLocations(ctx context.Context, req *remotesapi.GetDownloadLocsRequest) (*remotesapi.GetDownloadLocsResponse, error) { 152 logger := getReqLogger(rs.lgr, "GetDownloadLocations") 153 if err := ValidateGetDownloadLocsRequest(req); err != nil { 154 return nil, status.Error(codes.InvalidArgument, err.Error()) 155 } 156 repoPath := getRepoPath(req) 157 logger = logger.WithField(RepoPathField, repoPath) 158 defer func() { logger.Info("finished") }() 159 160 cs, err := rs.getStore(ctx, logger, repoPath) 161 if err != nil { 162 return nil, err 163 } 164 165 hashes, _ := remotestorage.ParseByteSlices(req.ChunkHashes) 166 167 prefix, err := rs.getRelativeStorePath(cs) 168 if err != nil { 169 logger.WithError(err).Error("error getting file store path for chunk store") 170 return nil, err 171 } 172 173 numHashes := len(hashes) 174 175 locations, err := cs.GetChunkLocationsWithPaths(hashes) 176 if err != nil { 177 logger.WithError(err).Error("error getting chunk locations for hashes") 178 return nil, err 179 } 180 181 md, _ := metadata.FromIncomingContext(ctx) 182 183 var locs []*remotesapi.DownloadLoc 184 numRanges := 0 185 for loc, hashToRange := range locations { 186 if len(hashToRange) == 0 { 187 continue 188 } 189 190 numRanges += len(hashToRange) 191 192 var ranges []*remotesapi.RangeChunk 193 for h, r := range hashToRange { 194 hCpy := h 195 ranges = append(ranges, &remotesapi.RangeChunk{Hash: hCpy[:], Offset: r.Offset, Length: r.Length}) 196 } 197 198 url := rs.getDownloadUrl(md, prefix+"/"+loc) 199 preurl := url.String() 200 url, err = rs.sealer.Seal(url) 201 if err != nil { 202 logger.WithError(err).Error("error sealing download url") 203 return nil, err 204 } 205 logger.WithFields(logrus.Fields{ 206 "url": preurl, 207 "ranges": ranges, 208 "sealed_url": url.String(), 209 }).Trace("generated sealed url") 210 211 getRange := &remotesapi.HttpGetRange{Url: url.String(), Ranges: ranges} 212 locs = append(locs, &remotesapi.DownloadLoc{Location: &remotesapi.DownloadLoc_HttpGetRange{HttpGetRange: getRange}}) 213 } 214 215 logger = logger.WithFields(logrus.Fields{ 216 "num_requested": numHashes, 217 "num_urls": len(locations), 218 "num_ranges": numRanges, 219 }) 220 221 return &remotesapi.GetDownloadLocsResponse{Locs: locs}, nil 222 } 223 224 func (rs *RemoteChunkStore) StreamDownloadLocations(stream remotesapi.ChunkStoreService_StreamDownloadLocationsServer) error { 225 ologger := getReqLogger(rs.lgr, "StreamDownloadLocations") 226 numMessages := 0 227 numHashes := 0 228 numUrls := 0 229 numRanges := 0 230 defer func() { 231 ologger.WithFields(logrus.Fields{ 232 "num_messages": numMessages, 233 "num_requested": numHashes, 234 "num_urls": numUrls, 235 "num_ranges": numRanges, 236 }).Info("finished") 237 }() 238 logger := ologger 239 240 md, _ := metadata.FromIncomingContext(stream.Context()) 241 242 var repoPath string 243 var cs RemoteSrvStore 244 var prefix string 245 for { 246 req, err := stream.Recv() 247 if err != nil { 248 if err == io.EOF { 249 return nil 250 } 251 return err 252 } 253 254 numMessages += 1 255 256 if err := ValidateGetDownloadLocsRequest(req); err != nil { 257 return status.Error(codes.InvalidArgument, err.Error()) 258 } 259 260 nextPath := getRepoPath(req) 261 if nextPath != repoPath { 262 repoPath = nextPath 263 logger = ologger.WithField(RepoPathField, repoPath) 264 cs, err = rs.getStore(stream.Context(), logger, repoPath) 265 if err != nil { 266 return err 267 } 268 prefix, err = rs.getRelativeStorePath(cs) 269 if err != nil { 270 logger.WithError(err).Error("error getting file store path for chunk store") 271 return err 272 } 273 } 274 275 hashes, _ := remotestorage.ParseByteSlices(req.ChunkHashes) 276 if err != nil { 277 return err 278 } 279 numHashes += len(hashes) 280 locations, err := cs.GetChunkLocationsWithPaths(hashes) 281 if err != nil { 282 logger.WithError(err).Error("error getting chunk locations for hashes") 283 return err 284 } 285 286 var locs []*remotesapi.DownloadLoc 287 for loc, hashToRange := range locations { 288 if len(hashToRange) == 0 { 289 continue 290 } 291 292 numUrls += 1 293 numRanges += len(hashToRange) 294 295 var ranges []*remotesapi.RangeChunk 296 for h, r := range hashToRange { 297 hCpy := h 298 ranges = append(ranges, &remotesapi.RangeChunk{Hash: hCpy[:], Offset: r.Offset, Length: r.Length}) 299 } 300 301 url := rs.getDownloadUrl(md, prefix+"/"+loc) 302 preurl := url.String() 303 url, err = rs.sealer.Seal(url) 304 if err != nil { 305 logger.WithError(err).Error("error sealing download url") 306 return err 307 } 308 logger.WithFields(logrus.Fields{ 309 "url": preurl, 310 "ranges": ranges, 311 "sealed_url": url.String(), 312 }).Trace("generated sealed url") 313 314 getRange := &remotesapi.HttpGetRange{Url: url.String(), Ranges: ranges} 315 locs = append(locs, &remotesapi.DownloadLoc{Location: &remotesapi.DownloadLoc_HttpGetRange{HttpGetRange: getRange}}) 316 } 317 318 if err := stream.Send(&remotesapi.GetDownloadLocsResponse{Locs: locs}); err != nil { 319 return err 320 } 321 } 322 } 323 324 func (rs *RemoteChunkStore) getHost(md metadata.MD) string { 325 host := rs.HttpHost 326 if strings.HasPrefix(rs.HttpHost, ":") { 327 hosts := md.Get(":authority") 328 if len(hosts) > 0 { 329 host = strings.Split(hosts[0], ":")[0] + rs.HttpHost 330 } 331 } else if rs.HttpHost == "" { 332 hosts := md.Get(":authority") 333 if len(hosts) > 0 { 334 host = hosts[0] 335 } 336 } 337 return host 338 } 339 340 func (rs *RemoteChunkStore) getDownloadUrl(md metadata.MD, path string) *url.URL { 341 host := rs.getHost(md) 342 return &url.URL{ 343 Scheme: rs.httpScheme, 344 Host: host, 345 Path: path, 346 } 347 } 348 349 func parseTableFileDetails(req *remotesapi.GetUploadLocsRequest) []*remotesapi.TableFileDetails { 350 tfd := req.GetTableFileDetails() 351 352 if len(tfd) == 0 { 353 _, hashToIdx := remotestorage.ParseByteSlices(req.TableFileHashes) 354 355 tfd = make([]*remotesapi.TableFileDetails, len(hashToIdx)) 356 for h, i := range hashToIdx { 357 tfd[i] = &remotesapi.TableFileDetails{ 358 Id: h[:], 359 ContentLength: 0, 360 ContentHash: nil, 361 } 362 } 363 } 364 365 return tfd 366 } 367 368 func (rs *RemoteChunkStore) GetUploadLocations(ctx context.Context, req *remotesapi.GetUploadLocsRequest) (*remotesapi.GetUploadLocsResponse, error) { 369 logger := getReqLogger(rs.lgr, "GetUploadLocations") 370 if err := ValidateGetUploadLocsRequest(req); err != nil { 371 return nil, status.Error(codes.InvalidArgument, err.Error()) 372 } 373 repoPath := getRepoPath(req) 374 logger = logger.WithField(RepoPathField, repoPath) 375 defer func() { logger.Info("finished") }() 376 377 _, err := rs.getStore(ctx, logger, repoPath) 378 if err != nil { 379 return nil, err 380 } 381 382 tfds := parseTableFileDetails(req) 383 384 md, _ := metadata.FromIncomingContext(ctx) 385 386 var locs []*remotesapi.UploadLoc 387 for _, tfd := range tfds { 388 h := hash.New(tfd.Id) 389 url := rs.getUploadUrl(md, repoPath, tfd) 390 url, err = rs.sealer.Seal(url) 391 if err != nil { 392 logger.WithError(err).Error("error sealing upload url") 393 return nil, status.Error(codes.Internal, "Failed to seal upload Url.") 394 } 395 396 loc := &remotesapi.UploadLoc_HttpPost{HttpPost: &remotesapi.HttpPostTableFile{Url: url.String()}} 397 locs = append(locs, &remotesapi.UploadLoc{TableFileHash: h[:], Location: loc}) 398 399 logger.WithFields(logrus.Fields{ 400 "table_file_hash": h.String(), 401 "url": url.String(), 402 }).Trace("sending upload location for table file") 403 } 404 405 logger = logger.WithFields(logrus.Fields{ 406 "num_urls": len(locs), 407 }) 408 409 return &remotesapi.GetUploadLocsResponse{Locs: locs}, nil 410 } 411 412 func (rs *RemoteChunkStore) getUploadUrl(md metadata.MD, repoPath string, tfd *remotesapi.TableFileDetails) *url.URL { 413 fileID := hash.New(tfd.Id).String() 414 params := url.Values{} 415 params.Add("num_chunks", strconv.Itoa(int(tfd.NumChunks))) 416 params.Add("content_length", strconv.Itoa(int(tfd.ContentLength))) 417 params.Add("content_hash", base64.RawURLEncoding.EncodeToString(tfd.ContentHash)) 418 return &url.URL{ 419 Scheme: rs.httpScheme, 420 Host: rs.getHost(md), 421 Path: fmt.Sprintf("%s/%s", repoPath, fileID), 422 RawQuery: params.Encode(), 423 } 424 } 425 426 func (rs *RemoteChunkStore) Rebase(ctx context.Context, req *remotesapi.RebaseRequest) (*remotesapi.RebaseResponse, error) { 427 logger := getReqLogger(rs.lgr, "Rebase") 428 if err := ValidateRebaseRequest(req); err != nil { 429 return nil, status.Error(codes.InvalidArgument, err.Error()) 430 } 431 repoPath := getRepoPath(req) 432 logger = logger.WithField(RepoPathField, repoPath) 433 defer func() { logger.Info("finished") }() 434 435 _, err := rs.getStore(ctx, logger, repoPath) 436 if err != nil { 437 return nil, err 438 } 439 440 return &remotesapi.RebaseResponse{}, nil 441 } 442 443 func (rs *RemoteChunkStore) Root(ctx context.Context, req *remotesapi.RootRequest) (*remotesapi.RootResponse, error) { 444 logger := getReqLogger(rs.lgr, "Root") 445 if err := ValidateRootRequest(req); err != nil { 446 return nil, status.Error(codes.InvalidArgument, err.Error()) 447 } 448 repoPath := getRepoPath(req) 449 logger = logger.WithField(RepoPathField, repoPath) 450 defer func() { logger.Info("finished") }() 451 452 cs, err := rs.getStore(ctx, logger, repoPath) 453 if err != nil { 454 return nil, err 455 } 456 457 h, err := cs.Root(ctx) 458 if err != nil { 459 logger.WithError(err).Error("error calling Root on chunk store.") 460 return nil, status.Error(codes.Internal, "Failed to get root") 461 } 462 463 return &remotesapi.RootResponse{RootHash: h[:]}, nil 464 } 465 466 func (rs *RemoteChunkStore) Commit(ctx context.Context, req *remotesapi.CommitRequest) (*remotesapi.CommitResponse, error) { 467 logger := getReqLogger(rs.lgr, "Commit") 468 if err := ValidateCommitRequest(req); err != nil { 469 return nil, status.Error(codes.InvalidArgument, err.Error()) 470 } 471 repoPath := getRepoPath(req) 472 logger = logger.WithField(RepoPathField, repoPath) 473 defer func() { logger.Info("finished") }() 474 475 cs, err := rs.getStore(ctx, logger, repoPath) 476 if err != nil { 477 return nil, err 478 } 479 480 updates := make(map[string]int) 481 for _, cti := range req.ChunkTableInfo { 482 updates[hash.New(cti.Hash).String()] = int(cti.ChunkCount) 483 } 484 485 err = cs.AddTableFilesToManifest(ctx, updates) 486 if err != nil { 487 logger.WithError(err).Error("error calling AddTableFilesToManifest") 488 return nil, status.Errorf(codes.Internal, "manifest update error: %v", err) 489 } 490 491 currHash := hash.New(req.Current) 492 lastHash := hash.New(req.Last) 493 494 var ok bool 495 ok, err = cs.Commit(ctx, currHash, lastHash) 496 if err != nil { 497 logger.WithError(err).WithFields(logrus.Fields{ 498 "last_hash": lastHash.String(), 499 "curr_hash": currHash.String(), 500 }).Error("error calling Commit") 501 return nil, status.Errorf(codes.Internal, "failed to commit: %v", err) 502 } 503 504 logger.Tracef("Commit success; moved from %s -> %s", lastHash.String(), currHash.String()) 505 return &remotesapi.CommitResponse{Success: ok}, nil 506 } 507 508 func (rs *RemoteChunkStore) GetRepoMetadata(ctx context.Context, req *remotesapi.GetRepoMetadataRequest) (*remotesapi.GetRepoMetadataResponse, error) { 509 logger := getReqLogger(rs.lgr, "GetRepoMetadata") 510 if err := ValidateGetRepoMetadataRequest(req); err != nil { 511 return nil, status.Error(codes.InvalidArgument, err.Error()) 512 } 513 514 repoPath := getRepoPath(req) 515 logger = logger.WithField(RepoPathField, repoPath) 516 defer func() { logger.Info("finished") }() 517 518 cs, err := rs.getOrCreateStore(ctx, logger, repoPath, req.ClientRepoFormat.NbfVersion) 519 if err != nil { 520 return nil, err 521 } 522 523 size, err := cs.Size(ctx) 524 if err != nil { 525 logger.WithError(err).Error("error calling Size") 526 return nil, err 527 } 528 529 return &remotesapi.GetRepoMetadataResponse{ 530 NbfVersion: cs.Version(), 531 NbsVersion: req.ClientRepoFormat.NbsVersion, 532 StorageSize: size, 533 PushConcurrencyControl: rs.concurrencyControl, 534 }, nil 535 } 536 537 func (rs *RemoteChunkStore) ListTableFiles(ctx context.Context, req *remotesapi.ListTableFilesRequest) (*remotesapi.ListTableFilesResponse, error) { 538 logger := getReqLogger(rs.lgr, "ListTableFiles") 539 if err := ValidateListTableFilesRequest(req); err != nil { 540 return nil, status.Error(codes.InvalidArgument, err.Error()) 541 } 542 repoPath := getRepoPath(req) 543 logger = logger.WithField(RepoPathField, repoPath) 544 defer func() { logger.Info("finished") }() 545 546 cs, err := rs.getStore(ctx, logger, repoPath) 547 if err != nil { 548 return nil, err 549 } 550 551 root, tables, appendixTables, err := cs.Sources(ctx) 552 if err != nil { 553 logger.WithError(err).Error("error getting chunk store Sources") 554 return nil, status.Error(codes.Internal, "failed to get sources") 555 } 556 557 md, _ := metadata.FromIncomingContext(ctx) 558 559 tableFileInfo, err := getTableFileInfo(logger, md, rs, tables, req, cs) 560 if err != nil { 561 logger.WithError(err).Error("error getting table file info") 562 return nil, err 563 } 564 565 appendixTableFileInfo, err := getTableFileInfo(logger, md, rs, appendixTables, req, cs) 566 if err != nil { 567 logger.WithError(err).Error("error getting appendix table file info") 568 return nil, err 569 } 570 571 logger = logger.WithFields(logrus.Fields{ 572 "num_table_files": len(tableFileInfo), 573 "num_appendix_table_files": len(appendixTableFileInfo), 574 }) 575 576 resp := &remotesapi.ListTableFilesResponse{ 577 RootHash: root[:], 578 TableFileInfo: tableFileInfo, 579 AppendixTableFileInfo: appendixTableFileInfo, 580 } 581 582 return resp, nil 583 } 584 585 func getTableFileInfo( 586 logger *logrus.Entry, 587 md metadata.MD, 588 rs *RemoteChunkStore, 589 tableList []chunks.TableFile, 590 req *remotesapi.ListTableFilesRequest, 591 cs RemoteSrvStore, 592 ) ([]*remotesapi.TableFileInfo, error) { 593 prefix, err := rs.getRelativeStorePath(cs) 594 if err != nil { 595 return nil, err 596 } 597 appendixTableFileInfo := make([]*remotesapi.TableFileInfo, 0) 598 for _, t := range tableList { 599 url := rs.getDownloadUrl(md, prefix+"/"+t.LocationPrefix()+t.FileID()) 600 url, err = rs.sealer.Seal(url) 601 if err != nil { 602 return nil, status.Error(codes.Internal, "failed to get seal download url for "+t.FileID()) 603 } 604 605 appendixTableFileInfo = append(appendixTableFileInfo, &remotesapi.TableFileInfo{ 606 FileId: t.FileID(), 607 NumChunks: uint32(t.NumChunks()), 608 Url: url.String(), 609 }) 610 } 611 return appendixTableFileInfo, nil 612 } 613 614 // AddTableFiles updates the remote manifest with new table files without modifying the root hash. 615 func (rs *RemoteChunkStore) AddTableFiles(ctx context.Context, req *remotesapi.AddTableFilesRequest) (*remotesapi.AddTableFilesResponse, error) { 616 logger := getReqLogger(rs.lgr, "AddTableFiles") 617 if err := ValidateAddTableFilesRequest(req); err != nil { 618 return nil, status.Error(codes.InvalidArgument, err.Error()) 619 } 620 repoPath := getRepoPath(req) 621 logger = logger.WithField(RepoPathField, repoPath) 622 defer func() { logger.Info("finished") }() 623 624 cs, err := rs.getStore(ctx, logger, repoPath) 625 if err != nil { 626 return nil, err 627 } 628 629 updates := make(map[string]int) 630 for _, cti := range req.ChunkTableInfo { 631 updates[hash.New(cti.Hash).String()] = int(cti.ChunkCount) 632 } 633 634 err = cs.AddTableFilesToManifest(ctx, updates) 635 if err != nil { 636 logger.WithError(err).Error("error occurred updating the manifest") 637 return nil, status.Error(codes.Internal, "manifest update error") 638 } 639 640 logger = logger.WithFields(logrus.Fields{ 641 "num_files": len(updates), 642 }) 643 644 return &remotesapi.AddTableFilesResponse{Success: true}, nil 645 } 646 647 func (rs *RemoteChunkStore) getStore(ctx context.Context, logger *logrus.Entry, repoPath string) (RemoteSrvStore, error) { 648 return rs.getOrCreateStore(ctx, logger, repoPath, types.Format_Default.VersionString()) 649 } 650 651 func (rs *RemoteChunkStore) getOrCreateStore(ctx context.Context, logger *logrus.Entry, repoPath, nbfVerStr string) (RemoteSrvStore, error) { 652 cs, err := rs.csCache.Get(ctx, repoPath, nbfVerStr) 653 if err != nil { 654 logger.WithError(err).Error("Failed to retrieve chunkstore") 655 if errors.Is(err, ErrUnimplemented) { 656 return nil, status.Error(codes.Unimplemented, err.Error()) 657 } 658 return nil, err 659 } 660 if cs == nil { 661 logger.Error("internal error getting chunk store; csCache.Get returned nil") 662 return nil, status.Error(codes.Internal, "Could not get chunkstore") 663 } 664 return cs, nil 665 } 666 667 var requestId int32 668 669 func incReqId() int { 670 return int(atomic.AddInt32(&requestId, 1)) 671 } 672 673 func getReqLogger(lgr *logrus.Entry, method string) *logrus.Entry { 674 lgr = lgr.WithFields(logrus.Fields{ 675 "method": method, 676 "request_num": strconv.Itoa(incReqId()), 677 }) 678 lgr.Info("starting request") 679 return lgr 680 } 681 682 type ReadOnlyChunkStore struct { 683 remotesapi.ChunkStoreServiceServer 684 } 685 686 func (rs ReadOnlyChunkStore) GetUploadLocations(ctx context.Context, req *remotesapi.GetUploadLocsRequest) (*remotesapi.GetUploadLocsResponse, error) { 687 return nil, status.Error(codes.PermissionDenied, "this server only provides read-only access") 688 } 689 690 func (rs ReadOnlyChunkStore) AddTableFiles(ctx context.Context, req *remotesapi.AddTableFilesRequest) (*remotesapi.AddTableFilesResponse, error) { 691 return nil, status.Error(codes.PermissionDenied, "this server only provides read-only access") 692 } 693 694 func (rs ReadOnlyChunkStore) Commit(ctx context.Context, req *remotesapi.CommitRequest) (*remotesapi.CommitResponse, error) { 695 return nil, status.Error(codes.PermissionDenied, "this server only provides read-only access") 696 }