git-lfs/tq/api.go
brian m. carlson 51a5ca3f68
tq: pass hash algorithm during batch requests
During a batch request, pass the hash algorithm we're using to the
remote server, and read the value, if any, that we get back.  If it is
not either absent or the string "sha256", fail, since that means that
the client and server don't agree on the proper hash algorithm.
2021-09-10 14:39:01 +00:00

119 lines
2.9 KiB
Go

package tq
import (
"fmt"
"time"
"github.com/git-lfs/git-lfs/v3/errors"
"github.com/git-lfs/git-lfs/v3/git"
"github.com/git-lfs/git-lfs/v3/lfsapi"
"github.com/git-lfs/git-lfs/v3/lfshttp"
"github.com/rubyist/tracerx"
)
type tqClient struct {
maxRetries int
*lfsapi.Client
}
type batchRef struct {
Name string `json:"name,omitempty"`
}
type batchRequest struct {
Operation string `json:"operation"`
Objects []*Transfer `json:"objects"`
TransferAdapterNames []string `json:"transfers,omitempty"`
Ref *batchRef `json:"ref"`
HashAlgorithm string `json:"hash_algo"`
}
type BatchResponse struct {
Objects []*Transfer `json:"objects"`
TransferAdapterName string `json:"transfer"`
HashAlgorithm string `json:"hash_algo"`
endpoint lfshttp.Endpoint
}
func Batch(m *Manifest, dir Direction, remote string, remoteRef *git.Ref, objects []*Transfer) (*BatchResponse, error) {
if len(objects) == 0 {
return &BatchResponse{}, nil
}
return m.batchClient().Batch(remote, &batchRequest{
Operation: dir.String(),
Objects: objects,
TransferAdapterNames: m.GetAdapterNames(dir),
Ref: &batchRef{Name: remoteRef.Refspec()},
HashAlgorithm: "sha256",
})
}
type BatchClient interface {
Batch(remote string, bReq *batchRequest) (*BatchResponse, error)
MaxRetries() int
SetMaxRetries(n int)
}
func (c *tqClient) MaxRetries() int {
return c.maxRetries
}
func (c *tqClient) SetMaxRetries(n int) {
c.maxRetries = n
}
func (c *tqClient) Batch(remote string, bReq *batchRequest) (*BatchResponse, error) {
bRes := &BatchResponse{}
if len(bReq.Objects) == 0 {
return bRes, nil
}
if len(bReq.TransferAdapterNames) == 1 && bReq.TransferAdapterNames[0] == "basic" {
bReq.TransferAdapterNames = nil
}
missing := make(map[string]bool)
for _, obj := range bReq.Objects {
missing[obj.Oid] = obj.Missing
}
bRes.endpoint = c.Endpoints.Endpoint(bReq.Operation, remote)
requestedAt := time.Now()
req, err := c.NewRequest("POST", bRes.endpoint, "objects/batch", bReq)
if err != nil {
return nil, errors.Wrap(err, "batch request")
}
tracerx.Printf("api: batch %d files", len(bReq.Objects))
req = c.Client.LogRequest(req, "lfs.batch")
res, err := c.DoAPIRequestWithAuth(remote, lfshttp.WithRetries(req, c.MaxRetries()))
if err != nil {
tracerx.Printf("api error: %s", err)
return nil, errors.Wrap(err, "batch response")
}
if err := lfshttp.DecodeJSON(res, bRes); err != nil {
return bRes, errors.Wrap(err, "batch response")
}
if bRes.HashAlgorithm != "" && bRes.HashAlgorithm != "sha256" {
return bRes, errors.Wrap(fmt.Errorf("unsupported hash algorithm"), "batch response")
}
if res.StatusCode != 200 {
return nil, lfshttp.NewStatusCodeError(res)
}
for _, obj := range bRes.Objects {
obj.Missing = missing[obj.Oid]
for _, a := range obj.Actions {
a.createdAt = requestedAt
}
}
return bRes, nil
}