Add descriptor to header conversion

Signed-off-by: Philip Laine <philip.laine@gmail.com>
This commit is contained in:
Philip Laine 2025-05-23 13:58:00 +02:00
parent e816a9ce84
commit 16f5d15eab
4 changed files with 69 additions and 26 deletions

View File

@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#888](https://github.com/spegel-org/spegel/pull/888) Refactor OCI events to support content events.
- [#890](https://github.com/spegel-org/spegel/pull/890) Refactor Containerd options to use config struct.
- [#896](https://github.com/spegel-org/spegel/pull/896) Rename package mux to httpx and refactor http helpers.
- [#897](https://github.com/spegel-org/spegel/pull/897) Add descriptor to header conversion.
### Deprecated

View File

@ -73,7 +73,7 @@ func TestStatusError(t *testing.T) {
rec := httptest.NewRecorder()
rec.WriteHeader(tt.statusCode)
rec.Header().Set("Content-Type", tt.contentType)
rec.Header().Set(HeaderContentType, tt.contentType)
rec.Body = bytes.NewBufferString(tt.body)
resp := &http.Response{

View File

@ -205,34 +205,10 @@ func (c *Client) fetch(ctx context.Context, method string, dist DistributionPath
return nil, ocispec.Descriptor{}, err
}
dgst := dist.Digest
dgstStr := resp.Header.Get(HeaderDockerDigest)
if dgstStr != "" {
dgst, err = digest.Parse(dgstStr)
if err != nil {
return nil, ocispec.Descriptor{}, err
}
}
if dgst == "" {
return nil, ocispec.Descriptor{}, errors.New("digest cannot be empty")
}
mt := resp.Header.Get(httpx.HeaderContentType)
if mt == "" {
return nil, ocispec.Descriptor{}, errors.New("content type header cannot be empty")
}
cl := resp.Header.Get(httpx.HeaderContentLength)
if cl == "" {
return nil, ocispec.Descriptor{}, errors.New("content length header cannot be empty")
}
size, err := strconv.ParseInt(cl, 10, 64)
desc, err := DescriptorFromHeader(resp.Header)
if err != nil {
return nil, ocispec.Descriptor{}, err
}
desc := ocispec.Descriptor{
Digest: dgst,
MediaType: mt,
Size: size,
}
return resp.Body, desc, nil
}
return nil, ocispec.Descriptor{}, errors.New("could not perform request")
@ -289,3 +265,34 @@ func getBearerToken(ctx context.Context, wwwAuth string, client *http.Client) (s
}
return tokenResp.Token, nil
}
func DescriptorFromHeader(header http.Header) (ocispec.Descriptor, error) {
mediaType := header.Get(httpx.HeaderContentType)
if mediaType == "" {
return ocispec.Descriptor{}, errors.New("content type cannot be empty")
}
contentLength := header.Get(httpx.HeaderContentLength)
if contentLength == "" {
return ocispec.Descriptor{}, errors.New("content length cannot be empty")
}
size, err := strconv.ParseInt(contentLength, 10, 64)
if err != nil {
return ocispec.Descriptor{}, err
}
dgst, err := digest.Parse(header.Get(HeaderDockerDigest))
if err != nil {
return ocispec.Descriptor{}, err
}
desc := ocispec.Descriptor{
MediaType: mediaType,
Size: size,
Digest: dgst,
}
return desc, nil
}
func WriteDescriptorToHeader(desc ocispec.Descriptor, header http.Header) {
header.Set(httpx.HeaderContentType, desc.MediaType)
header.Set(httpx.HeaderContentLength, strconv.FormatInt(desc.Size, 10))
header.Set(HeaderDockerDigest, desc.Digest.String())
}

View File

@ -96,3 +96,38 @@ func TestPull(t *testing.T) {
require.NotEmpty(t, pullResults)
}
func TestDescriptorHeader(t *testing.T) {
t.Parallel()
header := http.Header{}
desc := ocispec.Descriptor{
MediaType: "foo",
Size: 909,
Digest: digest.Digest("sha256:b6d6089ca6c395fd563c2084f5dd7bc56a2f5e6a81413558c5be0083287a77e9"),
}
WriteDescriptorToHeader(desc, header)
require.Equal(t, "foo", header.Get(httpx.HeaderContentType))
require.Equal(t, "909", header.Get(httpx.HeaderContentLength))
require.Equal(t, "sha256:b6d6089ca6c395fd563c2084f5dd7bc56a2f5e6a81413558c5be0083287a77e9", header.Get(HeaderDockerDigest))
headerDesc, err := DescriptorFromHeader(header)
require.NoError(t, err)
require.Equal(t, desc, headerDesc)
header = http.Header{}
_, err = DescriptorFromHeader(header)
require.EqualError(t, err, "content type cannot be empty")
header.Set(httpx.HeaderContentType, "test")
_, err = DescriptorFromHeader(header)
require.EqualError(t, err, "content length cannot be empty")
header.Set(httpx.HeaderContentLength, "wrong")
_, err = DescriptorFromHeader(header)
require.EqualError(t, err, "strconv.ParseInt: parsing \"wrong\": invalid syntax")
header.Set(httpx.HeaderContentLength, "250000")
_, err = DescriptorFromHeader(header)
require.EqualError(t, err, "invalid checksum digest format")
header.Set(HeaderDockerDigest, "foobar")
_, err = DescriptorFromHeader(header)
require.EqualError(t, err, "invalid checksum digest format")
}