145 lines
2.9 KiB
Go
145 lines
2.9 KiB
Go
/*
|
|
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
*
|
|
*
|
|
*/
|
|
|
|
package repocli
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"runtime"
|
|
|
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
)
|
|
|
|
type Loader struct {
|
|
cli *Client
|
|
}
|
|
|
|
func NewLoader(client *Client) *Loader {
|
|
return &Loader{
|
|
cli: client,
|
|
}
|
|
}
|
|
|
|
func (down *Loader) Pull(ctx context.Context, rawref, dir, osname, arch, variant string) error {
|
|
var err error
|
|
if osname == "" {
|
|
osname = runtime.GOOS
|
|
}
|
|
if arch == "" {
|
|
arch = runtime.GOARCH
|
|
}
|
|
|
|
ref, err := NewReferer(rawref)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rawrepo := ref.RawRepo()
|
|
accepts := []string{
|
|
MediaTypeDDMLv2,
|
|
MediaTypeDDMv2,
|
|
MediaTypeOIMv1,
|
|
MediaTypeOIIv1,
|
|
}
|
|
exist, mime, man, digstr, err := down.cli.GetRawManifest(ctx, ref.Raw(), accepts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exist {
|
|
err = errors.New("Manifest not found")
|
|
return err
|
|
}
|
|
tag := ref.Tag()
|
|
if mime == MediaTypeOIIv1 || mime == MediaTypeDDMLv2 {
|
|
var index ocispec.Index
|
|
err = json.Unmarshal(man, &index)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, descr := range index.Manifests {
|
|
if descr.Platform != nil {
|
|
var cond bool
|
|
cond = descr.Platform.Architecture == arch
|
|
cond = cond && descr.Platform.OS == osname
|
|
if variant != "" {
|
|
cond = cond && descr.Platform.Variant == variant
|
|
}
|
|
if cond {
|
|
tag = descr.Digest.String()
|
|
digstr = descr.Digest.String()
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if tag == "" {
|
|
err = errors.New("Manifest for required arch and OS not found")
|
|
return err
|
|
}
|
|
accepts := []string{
|
|
MediaTypeDDMv2,
|
|
MediaTypeOIMv1,
|
|
}
|
|
_, mime, man, digstr, err = down.cli.GetRawManifest(ctx, ref.RawWithTag(tag), accepts)
|
|
if err != nil {
|
|
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 {
|
|
return err
|
|
}
|
|
// Write image manifest
|
|
imager := NewEmptyImager(dir)
|
|
err = imager.WriteManifest(ctx, digstr, mime, man)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
layers := make([]ocispec.Descriptor, 0)
|
|
layers = append(layers, manifest.Config)
|
|
layers = append(layers, manifest.Layers...)
|
|
for _, layer := range layers {
|
|
wrapfunc := func() error {
|
|
tmpfile, err := os.CreateTemp(dir, "*.bin")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tmpfile.Close()
|
|
tmpname := tmpfile.Name()
|
|
defer os.Remove(tmpname)
|
|
|
|
digstr := layer.Digest.String()
|
|
exist, err := down.cli.GetBlob(ctx, rawrepo, tmpfile, digstr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exist {
|
|
err = errors.New("Layer not found")
|
|
return err
|
|
}
|
|
tmpfile.Seek(0, 0)
|
|
size := layer.Size
|
|
err = imager.WriteLayer(ctx, digstr, size, tmpfile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return err
|
|
}
|
|
err = wrapfunc()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return err
|
|
}
|