repocli: first version of pull & push image

This commit is contained in:
2026-03-10 21:19:19 +02:00
parent f85cda868a
commit 9bc3b77144
13 changed files with 61 additions and 90 deletions
+16 -15
View File
@@ -13,61 +13,62 @@ import (
ocidigest "github.com/opencontainers/go-digest"
)
func (cli *Client) GetRawManifest(ctx context.Context, rawrepo string) (bool, string, []byte, error) {
func (cli *Client) GetRawManifest(ctx context.Context, rawrepo string) (bool, string, []byte, string, error) {
var err error
var exist bool
var mime string
var man []byte
var digstr string
fmt.Printf("=== %s\n", rawrepo)
ref, err := NewReferer(rawrepo)
if err != nil {
return exist, mime, man, err
return exist, mime, man, digstr, err
}
uri := ref.ManifestEP()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return exist, mime, man, err
return exist, mime, man, digstr, err
}
req.Header.Set("User-Agent", cli.userAgent)
req.Header.Set("Accept", "*/*")
resp, err := cli.httpClient.Do(req)
if err != nil {
return exist, mime, man, err
return exist, mime, man, digstr, err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
return exist, mime, man, err
return exist, mime, man, digstr, err
}
if resp.StatusCode != http.StatusOK {
err := fmt.Errorf("Unxected response code %s", resp.Status)
return exist, mime, man, err
return exist, mime, man, digstr, err
}
digstr := resp.Header.Get("Docker-Content-Digest")
digstr = resp.Header.Get("Docker-Content-Digest")
if digstr == "" {
err := fmt.Errorf("Empty digest declaration")
return exist, mime, man, err
return exist, mime, man, digstr, err
}
contentLength := resp.Header.Get("Content-Length")
if contentLength == "" {
err = errors.New("Empty Content-Length header")
return exist, mime, man, err
return exist, mime, man, digstr, err
}
manSize, err := strconv.ParseInt(contentLength, 10, 64)
if err != nil {
return exist, mime, man, err
return exist, mime, man, digstr, err
}
mime = resp.Header.Get("Content-Type")
if mime == "" {
err := fmt.Errorf("Empty MIME type declaration")
return exist, mime, man, err
return exist, mime, man, digstr, err
}
digstr = strings.ToLower(digstr)
digobj, err := ocidigest.Parse(digstr)
if err != nil {
return exist, mime, man, err
return exist, mime, man, digstr, err
}
verifier := digobj.Verifier()
buffer := bytes.NewBuffer(nil)
@@ -75,13 +76,13 @@ func (cli *Client) GetRawManifest(ctx context.Context, rawrepo string) (bool, st
recSize, err := Copy(ctx, mwriter, resp.Body)
if manSize != recSize {
err := fmt.Errorf("Mismatch declared and actual size")
return exist, mime, man, err
return exist, mime, man, digstr, err
}
man = buffer.Bytes()
if !verifier.Verified() {
err := fmt.Errorf("Mismatch digest declaration and actual")
return exist, mime, man, err
return exist, mime, man, digstr, err
}
exist = true
return exist, mime, man, err
return exist, mime, man, digstr, err
}
+3 -2
View File
@@ -11,7 +11,7 @@ import (
"time"
)
func xxxTestClientGetManifest(t *testing.T) {
func TestClientGetManifest(t *testing.T) {
rawrepo := "mirror.gcr.io/alpine"
tags := []string{
"3.20.0",
@@ -20,11 +20,12 @@ func xxxTestClientGetManifest(t *testing.T) {
for _, tag := range tags {
cli := NewClient(nil, nil)
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
exist, mime, man, err := cli.GetRawManifest(ctx, rawrepo+":"+tag)
exist, mime, man, digstr, err := cli.GetRawManifest(ctx, rawrepo+":"+tag)
require.NoError(t, err)
require.True(t, exist)
fmt.Printf("Type: %s\n", mime)
fmt.Printf("Digest: %s\n", digstr)
buffer := bytes.NewBuffer(nil)
err = json.Indent(buffer, man, " ", " ")
require.NoError(t, err)
+17 -21
View File
@@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -35,9 +34,8 @@ func (down *Loader) Pull(ctx context.Context, rawref, dir, osname, arch string)
return err
}
rawrepo := ref.RawRepo()
tag := ref.Tag()
exist, mime, man, err := down.cli.GetRawManifest(ctx, ref.Raw())
exist, mime, man, digstr, err := down.cli.GetRawManifest(ctx, ref.Raw())
if err != nil {
return err
}
@@ -45,8 +43,7 @@ func (down *Loader) Pull(ctx context.Context, rawref, dir, osname, arch string)
err = errors.New("Manifest not found")
return err
}
var digstr string
tag := ref.Tag()
if mime == MediaTypeOIIv1 || mime == MediaTypeDDMLv2 {
var index ocispec.Index
err = json.Unmarshal(man, &index)
@@ -63,25 +60,24 @@ func (down *Loader) Pull(ctx context.Context, rawref, dir, osname, arch string)
}
}
}
}
if digstr == "" {
err = errors.New("Manifest for required arch and OS not found")
return err
}
fmt.Printf("Tag: %s\n", tag)
exist, mime, man, err = down.cli.GetRawManifest(ctx, ref.RawWithTag(digstr))
if err != nil {
return err
}
fmt.Printf("Mime: %s\n", mime)
if !exist {
err = errors.New("Manifest not found")
return err
}
if mime != MediaTypeOIMv1 && mime != MediaTypeDDMv2 {
if tag == "" {
err = errors.New("Manifest for required arch and OS not found")
return err
}
exist, mime, man, digstr, err = down.cli.GetRawManifest(ctx, ref.RawWithTag(tag))
if err != nil {
return err
}
if !exist {
err = errors.New("Manifest not found")
return err
}
} else if mime != MediaTypeOIMv1 && mime != MediaTypeDDMv2 {
err = errors.New("Unknown manifest media type")
return err
}
var manifest ocispec.Manifest
err = json.Unmarshal(man, &manifest)
if err != nil {
+1
View File
@@ -22,4 +22,5 @@ func xxxTestPullImage(t *testing.T) {
destdir := "aaa"
err := down.Pull(ctx, rawref, destdir, "linux", "amd64")
require.NoError(t, err)
}
+5 -3
View File
@@ -21,7 +21,7 @@ func (down *Loader) Push(ctx context.Context, rawref, dir, osname, arch string)
index := imager.Index()
for _, descr := range index.Manifests {
digstr := descr.Digest.String()
mime := descr.MediaType
_, _, mandata, err := imager.ReadManifest(ctx, digstr)
if err != nil {
return err
@@ -54,8 +54,10 @@ func (down *Loader) Push(ctx context.Context, rawref, dir, osname, arch string)
return err
}
}
err = down.cli.PutManifest(ctx, ref.Raw(), mandata, mime)
if err != nil {
return err
}
}
return err
}
+5 -5
View File
@@ -9,17 +9,17 @@ import (
ocidigest "github.com/opencontainers/go-digest"
)
func (cli *Client) PutManifest(ctx context.Context, rawrepo, tag string, man []byte, mime string) error {
func (cli *Client) PutManifest(ctx context.Context, rawref string, man []byte, mime string) error {
var err error
ref, err := NewReferer(rawrepo)
ref, err := NewReferer(rawref)
if err != nil {
return err
}
uri := ref.ManifestEP()
buffer := bytes.NewBuffer(man)
req, err := http.NewRequestWithContext(ctx, http.MethodPut, uri, buffer)
reader := bytes.NewReader(man)
req, err := http.NewRequestWithContext(ctx, http.MethodPut, uri, reader)
if err != nil {
return err
}
@@ -33,7 +33,7 @@ func (cli *Client) PutManifest(ctx context.Context, rawrepo, tag string, man []b
return err
}
resp.Body.Close()
if resp.StatusCode != http.StatusAccepted {
if resp.StatusCode != http.StatusAccepted && resp.StatusCode != http.StatusCreated {
err = fmt.Errorf("Manifest not accepted, code %d", resp.StatusCode)
return err
}