58 lines
2.0 KiB
Go
58 lines
2.0 KiB
Go
package artifacts
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
)
|
|
|
|
// blobBound is the most GetBlob reads: a templates archive is a few kilobytes; anything near this is no archive a
|
|
// take-in reads text from.
|
|
const blobBound = 64 << 20
|
|
|
|
// GetBlob is the archive a recorded reference names (`artifact-store://<repository>/blobs/sha256:<hex>`), read whole
|
|
// and checked against the digest it names: bytes that hash to another digest are refused, never read (R4e: the
|
|
// controller reads a templates archive by the reference of a builder's done key). Gone when the store does not hold
|
|
// it; ErrNotOurs for a reference outside the store or one that is not an archive.
|
|
func (s Store) GetBlob(ctx context.Context, reference string) ([]byte, error) {
|
|
repository, digest, archive, err := s.archive(reference)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !archive {
|
|
return nil, fmt.Errorf("%w: %s is not an archive", ErrNotOurs, reference)
|
|
}
|
|
request, err := http.NewRequestWithContext(ctx, http.MethodGet, s.url(repository, "blobs", digest), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
response, err := s.client().Do(request)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot reach the artifact store at %s: %w", s.Address, err)
|
|
}
|
|
defer response.Body.Close()
|
|
switch response.StatusCode {
|
|
case http.StatusOK:
|
|
case http.StatusNotFound:
|
|
return nil, Gone
|
|
default:
|
|
return nil, fmt.Errorf("the artifact store answered %s for %s/blobs/%s", response.Status, repository, digest)
|
|
}
|
|
body, err := io.ReadAll(io.LimitReader(response.Body, blobBound+1))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("reading %s/blobs/%s: %w", repository, digest, err)
|
|
}
|
|
if len(body) > blobBound {
|
|
return nil, fmt.Errorf("%s/blobs/%s is larger than %d bytes, which no archive read here is", repository, digest, blobBound)
|
|
}
|
|
sum := sha256.Sum256(body)
|
|
if got := "sha256:" + hex.EncodeToString(sum[:]); got != digest {
|
|
return nil, fmt.Errorf("the artifact store answered %s/blobs/%s with bytes of the digest %s: refused", repository,
|
|
digest, got)
|
|
}
|
|
return body, nil
|
|
}
|