1package info 2 3import ( 4 "context" 5 "fmt" 6 7 "gitlab.com/gitlab-org/gitaly/v14/internal/helper" 8 "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" 9 "golang.org/x/sync/errgroup" 10 "google.golang.org/grpc" 11) 12 13// RepositoryReplicas returns a list of repositories that includes the checksum of the primary as well as the replicas 14func (s *Server) RepositoryReplicas(ctx context.Context, in *gitalypb.RepositoryReplicasRequest) (*gitalypb.RepositoryReplicasResponse, error) { 15 virtualStorage := in.GetRepository().GetStorageName() 16 relativePath := in.GetRepository().GetRelativePath() 17 18 primary, err := s.primaryGetter.GetPrimary(ctx, virtualStorage, relativePath) 19 if err != nil { 20 return nil, fmt.Errorf("get primary: %w", err) 21 } 22 23 assignments, err := s.assignmentStore.GetHostAssignments(ctx, virtualStorage, relativePath) 24 if err != nil { 25 return nil, fmt.Errorf("get host assignments: %w", err) 26 } 27 28 secondaries := make([]string, 0, len(assignments)-1) 29 primaryIsAssigned := false 30 for _, assignment := range assignments { 31 if primary == assignment { 32 primaryIsAssigned = true 33 continue 34 } 35 36 secondaries = append(secondaries, assignment) 37 } 38 39 if !primaryIsAssigned { 40 return nil, fmt.Errorf("primary %q is not an assigned host", primary) 41 } 42 43 var resp gitalypb.RepositoryReplicasResponse 44 45 if resp.Primary, err = s.getRepositoryDetails(ctx, virtualStorage, primary, relativePath); err != nil { 46 return nil, helper.ErrInternal(err) 47 } 48 49 resp.Replicas = make([]*gitalypb.RepositoryReplicasResponse_RepositoryDetails, len(secondaries)) 50 51 g, ctx := errgroup.WithContext(ctx) 52 53 for i, storage := range secondaries { 54 i := i // rescoping 55 storage := storage // rescoping 56 g.Go(func() error { 57 var err error 58 resp.Replicas[i], err = s.getRepositoryDetails(ctx, virtualStorage, storage, relativePath) 59 return err 60 }) 61 } 62 63 if err := g.Wait(); err != nil { 64 return nil, helper.ErrInternal(err) 65 } 66 67 return &resp, nil 68} 69 70func (s *Server) getRepositoryDetails(ctx context.Context, virtualStorage, storage, relativePath string) (*gitalypb.RepositoryReplicasResponse_RepositoryDetails, error) { 71 conn, ok := s.conns[virtualStorage][storage] 72 if !ok { 73 return nil, fmt.Errorf("no connection to %q/%q", virtualStorage, storage) 74 } 75 76 return getChecksum( 77 ctx, 78 &gitalypb.Repository{ 79 StorageName: storage, 80 RelativePath: relativePath, 81 }, conn) 82} 83 84func getChecksum(ctx context.Context, repo *gitalypb.Repository, cc *grpc.ClientConn) (*gitalypb.RepositoryReplicasResponse_RepositoryDetails, error) { 85 client := gitalypb.NewRepositoryServiceClient(cc) 86 87 resp, err := client.CalculateChecksum(ctx, 88 &gitalypb.CalculateChecksumRequest{ 89 Repository: repo, 90 }) 91 if err != nil { 92 return nil, err 93 } 94 95 return &gitalypb.RepositoryReplicasResponse_RepositoryDetails{ 96 Repository: repo, 97 Checksum: resp.GetChecksum(), 98 }, nil 99} 100