Files

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
}