podman/pkg/machine/ocipull/versioned.go

170 lines
4.7 KiB
Go

package ocipull
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v5/pkg/machine/compression"
"github.com/containers/podman/v5/pkg/machine/define"
"github.com/containers/podman/v5/utils"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
)
type Versioned struct {
blob *types.BlobInfo
blobDirPath string
cacheDir string
ctx context.Context
imageName string
machineImageDir string
machineVersion *OSVersion
vmName string
vmType string
finalPath *define.VMFile
}
func NewVersioned(ctx context.Context, machineImageDir *define.VMFile, vmName string, vmType string, finalPath *define.VMFile) (*Versioned, error) {
imageCacheDir := filepath.Join(machineImageDir.GetPath(), "cache")
if err := os.MkdirAll(imageCacheDir, 0777); err != nil {
return nil, err
}
o := getVersion()
return &Versioned{ctx: ctx, cacheDir: imageCacheDir, machineImageDir: machineImageDir.GetPath(), machineVersion: o, vmName: vmName, vmType: vmType, finalPath: finalPath}, nil
}
func (d *Versioned) LocalBlob() *types.BlobInfo {
return d.blob
}
func (d *Versioned) DiskEndpoint() string {
return d.machineVersion.diskImage(d.vmType)
}
func (d *Versioned) versionedOCICacheDir() string {
return filepath.Join(d.cacheDir, d.machineVersion.majorMinor())
}
func (d *Versioned) identifyImageNameFromOCIDir() (string, error) {
imageManifest, err := ReadImageManifestFromOCIPath(d.ctx, d.versionedOCICacheDir())
if err != nil {
return "", err
}
if len(imageManifest.Layers) > 1 {
return "", fmt.Errorf("podman machine images can have only one layer: %d found", len(imageManifest.Layers))
}
path := filepath.Join(d.versionedOCICacheDir(), "blobs", "sha256", imageManifest.Layers[0].Digest.Hex())
return findTarComponent(path)
}
func (d *Versioned) pull(path string) error {
fmt.Printf("Pulling %s\n", d.DiskEndpoint())
logrus.Debugf("pulling %s to %s", d.DiskEndpoint(), path)
return Pull(d.ctx, d.DiskEndpoint(), path, PullOptions{})
}
func (d *Versioned) Pull() error {
var (
err error
isUpdatable bool
localBlob *types.BlobInfo
remoteDescriptor *v1.Descriptor
)
remoteDiskImage := d.machineVersion.diskImage(d.vmType)
logrus.Debugf("podman disk image name: %s", remoteDiskImage)
// is there a valid oci dir in our cache
hasCache := d.localOCIDirExists()
if hasCache {
logrus.Debug("checking remote registry")
remoteDescriptor, err = GetRemoteDescriptor(d.ctx, remoteDiskImage)
if err != nil {
return err
}
logrus.Debugf("working with local cache: %s", d.versionedOCICacheDir())
localBlob, err = GetLocalBlob(d.ctx, d.versionedOCICacheDir())
if err != nil {
return err
}
// determine if the local is same as remote
if remoteDescriptor.Digest.Hex() != localBlob.Digest.Hex() {
logrus.Debugf("new image is available: %s", remoteDescriptor.Digest.Hex())
isUpdatable = true
}
}
if !hasCache || isUpdatable {
if hasCache {
if err := utils.GuardedRemoveAll(d.versionedOCICacheDir()); err != nil {
return err
}
}
if err := d.pull(d.versionedOCICacheDir()); err != nil {
return err
}
}
imageName, err := d.identifyImageNameFromOCIDir()
if err != nil {
return err
}
logrus.Debugf("image name: %s", imageName)
d.imageName = imageName
if localBlob == nil {
localBlob, err = GetLocalBlob(d.ctx, d.versionedOCICacheDir())
if err != nil {
return err
}
}
d.blob = localBlob
d.blobDirPath = d.versionedOCICacheDir()
logrus.Debugf("local oci disk image blob: %s", d.localOCIDiskImageDir(localBlob))
return nil
}
func (d *Versioned) Unpack() (*define.VMFile, error) {
tbPath := localOCIDiskImageDir(d.blobDirPath, d.blob)
unpackedFile, err := unpackOCIDir(tbPath, d.machineImageDir)
if err != nil {
return nil, err
}
d.imageName = unpackedFile.GetPath()
return unpackedFile, nil
}
func (d *Versioned) Decompress(compressedFile *define.VMFile) error {
return compression.Decompress(compressedFile, d.finalPath.GetPath())
}
func (d *Versioned) localOCIDiskImageDir(localBlob *types.BlobInfo) string {
return filepath.Join(d.versionedOCICacheDir(), "blobs", "sha256", localBlob.Digest.Hex())
}
func (d *Versioned) localOCIDirExists() bool {
_, indexErr := os.Stat(filepath.Join(d.versionedOCICacheDir(), "index.json"))
return indexErr == nil
}
func (d *Versioned) Get() error {
if err := d.Pull(); err != nil {
return err
}
unpacked, err := d.Unpack()
if err != nil {
return err
}
defer func() {
logrus.Debugf("cleaning up %q", unpacked.GetPath())
if err := unpacked.Delete(); err != nil {
logrus.Errorf("unable to delete local compressed file %q:%v", unpacked.GetPath(), err)
}
}()
return d.Decompress(unpacked)
}