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 }