2024-01-05 08:23:13 -06:00
|
|
|
package stdpull
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"io/fs"
|
|
|
|
|
"net/http"
|
|
|
|
|
url2 "net/url"
|
|
|
|
|
"os"
|
|
|
|
|
"path"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
|
2025-10-23 08:18:28 -04:00
|
|
|
"github.com/containers/podman/v6/pkg/machine/compression"
|
|
|
|
|
"github.com/containers/podman/v6/pkg/machine/define"
|
|
|
|
|
"github.com/containers/podman/v6/utils"
|
2024-01-05 08:23:13 -06:00
|
|
|
"github.com/sirupsen/logrus"
|
2025-09-01 10:52:33 +02:00
|
|
|
"go.podman.io/storage/pkg/fileutils"
|
2024-01-05 08:23:13 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type DiskFromURL struct {
|
|
|
|
|
u *url2.URL
|
|
|
|
|
finalPath *define.VMFile
|
|
|
|
|
tempLocation *define.VMFile
|
Clean machine pull cache
Cache cleanups only happen if there is a cache miss, and we need to pull a new image
For quay.io/podman/machine-os, we remove all old images from the cache dir. This means we will delete any file that exists in the cache dir; this should be safe to do since the machine pull code should be the only thing touching this cache dir. OCI machine images will always have a different manifest, and won’t be updated with the same manifest, so if the version moves on, there isn’t a reason to keep the old version in the cache, it really doesn’t change.
For Fedora (WSL), we use the cache, so we go through the cache dir and remove any old cached images, on a cache miss. We also switch to using ~/.local/share/containers/podman/machine/wsl/cache as the cache dir rather than ~/.local/share/containers/podman/machine/wsl. Both these behaviors existed in v4.9, but are now added back into 5.x.
For generic files pulled from a URL or a non-default OCI image, we shouldn’t actually cache, so we delete the pulled file immediately after creating a machine image. This restores the behavior from v4.9.
For generic files from a local path, the original file will never be cleaned up
Unsure how to test, so:
[NO NEW TESTS NEEDED]
Signed-off-by: Ashley Cui <acui@redhat.com>
2024-04-25 09:32:57 -04:00
|
|
|
cache bool
|
2024-01-05 08:23:13 -06:00
|
|
|
}
|
|
|
|
|
|
Clean machine pull cache
Cache cleanups only happen if there is a cache miss, and we need to pull a new image
For quay.io/podman/machine-os, we remove all old images from the cache dir. This means we will delete any file that exists in the cache dir; this should be safe to do since the machine pull code should be the only thing touching this cache dir. OCI machine images will always have a different manifest, and won’t be updated with the same manifest, so if the version moves on, there isn’t a reason to keep the old version in the cache, it really doesn’t change.
For Fedora (WSL), we use the cache, so we go through the cache dir and remove any old cached images, on a cache miss. We also switch to using ~/.local/share/containers/podman/machine/wsl/cache as the cache dir rather than ~/.local/share/containers/podman/machine/wsl. Both these behaviors existed in v4.9, but are now added back into 5.x.
For generic files pulled from a URL or a non-default OCI image, we shouldn’t actually cache, so we delete the pulled file immediately after creating a machine image. This restores the behavior from v4.9.
For generic files from a local path, the original file will never be cleaned up
Unsure how to test, so:
[NO NEW TESTS NEEDED]
Signed-off-by: Ashley Cui <acui@redhat.com>
2024-04-25 09:32:57 -04:00
|
|
|
func NewDiskFromURL(inputPath string, finalPath *define.VMFile, tempDir *define.VMFile, optionalTempFileName *string, cache bool) (*DiskFromURL, error) {
|
2025-11-11 12:12:42 +01:00
|
|
|
var err error
|
2024-01-05 08:23:13 -06:00
|
|
|
u, err := url2.Parse(inputPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure the temporary location exists before we get too deep
|
2024-04-10 18:27:43 +02:00
|
|
|
if err := fileutils.Exists(tempDir.GetPath()); err != nil {
|
2024-01-05 08:23:13 -06:00
|
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
|
|
|
return nil, fmt.Errorf("temporary download directory %s does not exist", tempDir.GetPath())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
remoteImageName := path.Base(inputPath)
|
2024-02-01 09:00:36 -06:00
|
|
|
if optionalTempFileName != nil {
|
|
|
|
|
remoteImageName = *optionalTempFileName
|
|
|
|
|
}
|
2024-01-05 08:23:13 -06:00
|
|
|
if remoteImageName == "" {
|
|
|
|
|
return nil, fmt.Errorf("invalid url: unable to determine image name in %q", inputPath)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tempLocation, err := tempDir.AppendToNewVMFile(remoteImageName, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &DiskFromURL{
|
|
|
|
|
u: u,
|
|
|
|
|
finalPath: finalPath,
|
|
|
|
|
tempLocation: tempLocation,
|
Clean machine pull cache
Cache cleanups only happen if there is a cache miss, and we need to pull a new image
For quay.io/podman/machine-os, we remove all old images from the cache dir. This means we will delete any file that exists in the cache dir; this should be safe to do since the machine pull code should be the only thing touching this cache dir. OCI machine images will always have a different manifest, and won’t be updated with the same manifest, so if the version moves on, there isn’t a reason to keep the old version in the cache, it really doesn’t change.
For Fedora (WSL), we use the cache, so we go through the cache dir and remove any old cached images, on a cache miss. We also switch to using ~/.local/share/containers/podman/machine/wsl/cache as the cache dir rather than ~/.local/share/containers/podman/machine/wsl. Both these behaviors existed in v4.9, but are now added back into 5.x.
For generic files pulled from a URL or a non-default OCI image, we shouldn’t actually cache, so we delete the pulled file immediately after creating a machine image. This restores the behavior from v4.9.
For generic files from a local path, the original file will never be cleaned up
Unsure how to test, so:
[NO NEW TESTS NEEDED]
Signed-off-by: Ashley Cui <acui@redhat.com>
2024-04-25 09:32:57 -04:00
|
|
|
cache: cache,
|
2024-01-05 08:23:13 -06:00
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *DiskFromURL) Get() error {
|
|
|
|
|
// this fetches the image and writes it to the temporary location
|
|
|
|
|
if err := d.pull(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
Clean machine pull cache
Cache cleanups only happen if there is a cache miss, and we need to pull a new image
For quay.io/podman/machine-os, we remove all old images from the cache dir. This means we will delete any file that exists in the cache dir; this should be safe to do since the machine pull code should be the only thing touching this cache dir. OCI machine images will always have a different manifest, and won’t be updated with the same manifest, so if the version moves on, there isn’t a reason to keep the old version in the cache, it really doesn’t change.
For Fedora (WSL), we use the cache, so we go through the cache dir and remove any old cached images, on a cache miss. We also switch to using ~/.local/share/containers/podman/machine/wsl/cache as the cache dir rather than ~/.local/share/containers/podman/machine/wsl. Both these behaviors existed in v4.9, but are now added back into 5.x.
For generic files pulled from a URL or a non-default OCI image, we shouldn’t actually cache, so we delete the pulled file immediately after creating a machine image. This restores the behavior from v4.9.
For generic files from a local path, the original file will never be cleaned up
Unsure how to test, so:
[NO NEW TESTS NEEDED]
Signed-off-by: Ashley Cui <acui@redhat.com>
2024-04-25 09:32:57 -04:00
|
|
|
if !d.cache {
|
|
|
|
|
defer func() {
|
|
|
|
|
if err := utils.GuardedRemoveAll(d.tempLocation.GetPath()); err != nil {
|
|
|
|
|
if !errors.Is(err, os.ErrNotExist) {
|
|
|
|
|
logrus.Warn("failed to clean machine image cache: ", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-04 15:02:35 -06:00
|
|
|
logrus.Debugf("decompressing (if needed) %s to %s", d.tempLocation.GetPath(), d.finalPath.GetPath())
|
2024-01-05 08:23:13 -06:00
|
|
|
return compression.Decompress(d.tempLocation, d.finalPath.GetPath())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *DiskFromURL) pull() error {
|
|
|
|
|
out, err := os.Create(d.tempLocation.GetPath())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer func() {
|
|
|
|
|
if err := out.Close(); err != nil {
|
|
|
|
|
logrus.Error(err)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
resp, err := http.Get(d.u.String())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer func() {
|
|
|
|
|
if err := resp.Body.Close(); err != nil {
|
|
|
|
|
logrus.Error(err)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
|
|
|
return fmt.Errorf("downloading VM image %s: %s", d.u.String(), resp.Status)
|
|
|
|
|
}
|
|
|
|
|
size := resp.ContentLength
|
|
|
|
|
prefix := "Downloading VM image: " + filepath.Base(d.tempLocation.GetPath())
|
|
|
|
|
onComplete := prefix + ": done"
|
|
|
|
|
|
|
|
|
|
p, bar := utils.ProgressBar(prefix, size, onComplete)
|
|
|
|
|
|
|
|
|
|
proxyReader := bar.ProxyReader(resp.Body)
|
|
|
|
|
defer func() {
|
|
|
|
|
if err := proxyReader.Close(); err != nil {
|
|
|
|
|
logrus.Error(err)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
if _, err := io.Copy(out, proxyReader); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.Wait()
|
|
|
|
|
return nil
|
|
|
|
|
}
|