github.com/containers/podman/v4@v4.9.4/pkg/machine/ocipull/versioned.go (about)

     1  package ocipull
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/containers/image/v5/types"
    11  	"github.com/containers/podman/v4/pkg/machine/compression"
    12  	"github.com/containers/podman/v4/pkg/machine/define"
    13  	"github.com/containers/podman/v4/utils"
    14  	v1 "github.com/opencontainers/image-spec/specs-go/v1"
    15  	"github.com/sirupsen/logrus"
    16  )
    17  
    18  type Versioned struct {
    19  	blob            *types.BlobInfo
    20  	blobDirPath     string
    21  	cacheDir        string
    22  	ctx             context.Context
    23  	imageFormat     define.ImageFormat
    24  	imageName       string
    25  	machineImageDir string
    26  	machineVersion  *OSVersion
    27  	vmName          string
    28  }
    29  
    30  func NewVersioned(ctx context.Context, machineImageDir, vmName string) (*Versioned, error) {
    31  	imageCacheDir := filepath.Join(machineImageDir, "cache")
    32  	if err := os.MkdirAll(imageCacheDir, 0777); err != nil {
    33  		return nil, err
    34  	}
    35  	o := getVersion()
    36  	return &Versioned{ctx: ctx, cacheDir: imageCacheDir, machineImageDir: machineImageDir, machineVersion: o, vmName: vmName}, nil
    37  }
    38  
    39  func (d *Versioned) LocalBlob() *types.BlobInfo {
    40  	return d.blob
    41  }
    42  
    43  func (d *Versioned) DiskEndpoint() string {
    44  	return d.machineVersion.diskImage(d.imageFormat)
    45  }
    46  
    47  func (d *Versioned) versionedOCICacheDir() string {
    48  	return filepath.Join(d.cacheDir, d.machineVersion.majorMinor())
    49  }
    50  
    51  func (d *Versioned) identifyImageNameFromOCIDir() (string, error) {
    52  	imageManifest, err := ReadImageManifestFromOCIPath(d.ctx, d.versionedOCICacheDir())
    53  	if err != nil {
    54  		return "", err
    55  	}
    56  	if len(imageManifest.Layers) > 1 {
    57  		return "", fmt.Errorf("podman machine images can have only one layer: %d found", len(imageManifest.Layers))
    58  	}
    59  	path := filepath.Join(d.versionedOCICacheDir(), "blobs", "sha256", imageManifest.Layers[0].Digest.Hex())
    60  	return findTarComponent(path)
    61  }
    62  
    63  func (d *Versioned) pull(path string) error {
    64  	fmt.Printf("Pulling %s\n", d.DiskEndpoint())
    65  	logrus.Debugf("pulling %s to %s", d.DiskEndpoint(), path)
    66  	return Pull(d.ctx, d.DiskEndpoint(), path, PullOptions{})
    67  }
    68  
    69  func (d *Versioned) Pull() error {
    70  	var (
    71  		err              error
    72  		isUpdatable      bool
    73  		localBlob        *types.BlobInfo
    74  		remoteDescriptor *v1.Descriptor
    75  	)
    76  
    77  	remoteDiskImage := d.machineVersion.diskImage(define.Qcow)
    78  	logrus.Debugf("podman disk image name: %s", remoteDiskImage)
    79  
    80  	// is there a valid oci dir in our cache
    81  	hasCache := d.localOCIDirExists()
    82  
    83  	if hasCache {
    84  		logrus.Debug("checking remote registry")
    85  		remoteDescriptor, err = GetRemoteDescriptor(d.ctx, remoteDiskImage)
    86  		if err != nil {
    87  			return err
    88  		}
    89  		logrus.Debugf("working with local cache: %s", d.versionedOCICacheDir())
    90  		localBlob, err = GetLocalBlob(d.ctx, d.versionedOCICacheDir())
    91  		if err != nil {
    92  			return err
    93  		}
    94  		// determine if the local is same as remote
    95  		if remoteDescriptor.Digest.Hex() != localBlob.Digest.Hex() {
    96  			logrus.Debugf("new image is available: %s", remoteDescriptor.Digest.Hex())
    97  			isUpdatable = true
    98  		}
    99  	}
   100  	if !hasCache || isUpdatable {
   101  		if hasCache {
   102  			if err := utils.GuardedRemoveAll(d.versionedOCICacheDir()); err != nil {
   103  				return err
   104  			}
   105  		}
   106  		if err := d.pull(d.versionedOCICacheDir()); err != nil {
   107  			return err
   108  		}
   109  	}
   110  	imageName, err := d.identifyImageNameFromOCIDir()
   111  	if err != nil {
   112  		return err
   113  	}
   114  	logrus.Debugf("image name: %s", imageName)
   115  	d.imageName = imageName
   116  
   117  	if localBlob == nil {
   118  		localBlob, err = GetLocalBlob(d.ctx, d.versionedOCICacheDir())
   119  		if err != nil {
   120  			return err
   121  		}
   122  	}
   123  	d.blob = localBlob
   124  	d.blobDirPath = d.versionedOCICacheDir()
   125  	logrus.Debugf("local oci disk image blob: %s", d.localOCIDiskImageDir(localBlob))
   126  	return nil
   127  }
   128  
   129  func (d *Versioned) Unpack() (*define.VMFile, error) {
   130  	tbPath := localOCIDiskImageDir(d.blobDirPath, d.blob)
   131  	unpackedFile, err := unpackOCIDir(tbPath, d.machineImageDir)
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  	d.imageName = unpackedFile.GetPath()
   136  	return unpackedFile, nil
   137  }
   138  
   139  func (d *Versioned) Decompress(compressedFile *define.VMFile) (*define.VMFile, error) {
   140  	imageCompression := compression.KindFromFile(d.imageName)
   141  	strippedImageName := strings.TrimSuffix(d.imageName, fmt.Sprintf(".%s", imageCompression.String()))
   142  	finalName := finalFQImagePathName(d.vmName, strippedImageName)
   143  	if err := compression.Decompress(compressedFile, finalName); err != nil {
   144  		return nil, err
   145  	}
   146  	return define.NewMachineFile(finalName, nil)
   147  }
   148  
   149  func (d *Versioned) localOCIDiskImageDir(localBlob *types.BlobInfo) string {
   150  	return filepath.Join(d.versionedOCICacheDir(), "blobs", "sha256", localBlob.Digest.Hex())
   151  }
   152  
   153  func (d *Versioned) localOCIDirExists() bool {
   154  	_, indexErr := os.Stat(filepath.Join(d.versionedOCICacheDir(), "index.json"))
   155  	return indexErr == nil
   156  }