github.com/containers/podman/v4@v4.9.4/pkg/machine/wsl/fedora.go (about)

     1  //go:build windows
     2  // +build windows
     3  
     4  package wsl
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"net/http"
    11  	"net/url"
    12  	"os"
    13  	"path"
    14  	"path/filepath"
    15  	"strings"
    16  	"time"
    17  
    18  	"github.com/containers/podman/v4/pkg/machine"
    19  	"github.com/containers/podman/v4/pkg/machine/define"
    20  )
    21  
    22  const (
    23  	githubX86ReleaseURL = "https://github.com/containers/podman-wsl-fedora/releases/latest/download/rootfs.tar.xz"
    24  	githubArmReleaseURL = "https://github.com/containers/podman-wsl-fedora-arm/releases/latest/download/rootfs.tar.xz"
    25  )
    26  
    27  type FedoraDownload struct {
    28  	machine.Download
    29  }
    30  
    31  func NewFedoraDownloader(vmType machine.VMType, vmName, releaseStream string) (machine.DistributionDownload, error) {
    32  	downloadURL, version, arch, size, err := getFedoraDownload()
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  
    37  	cacheDir, err := machine.GetCacheDir(vmType)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	imageName := fmt.Sprintf("fedora-podman-%s-%s.tar.xz", arch, version)
    43  
    44  	f := FedoraDownload{
    45  		Download: machine.Download{
    46  			Arch:      machine.GetFcosArch(),
    47  			Artifact:  define.None,
    48  			CacheDir:  cacheDir,
    49  			Format:    define.Tar,
    50  			ImageName: imageName,
    51  			LocalPath: filepath.Join(cacheDir, imageName),
    52  			URL:       downloadURL,
    53  			VMName:    vmName,
    54  			Size:      size,
    55  		},
    56  	}
    57  	dataDir, err := machine.GetDataDir(vmType)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	f.Download.LocalUncompressedFile = f.GetLocalUncompressedFile(dataDir)
    62  	return f, nil
    63  }
    64  
    65  func (f FedoraDownload) Get() *machine.Download {
    66  	return &f.Download
    67  }
    68  
    69  func (f FedoraDownload) HasUsableCache() (bool, error) {
    70  	info, err := os.Stat(f.LocalPath)
    71  	if err != nil {
    72  		if errors.Is(err, os.ErrNotExist) {
    73  			return false, nil
    74  		}
    75  		return false, err
    76  	}
    77  	return info.Size() == f.Size, nil
    78  }
    79  
    80  func (f FedoraDownload) CleanCache() error {
    81  	// Set cached image to expire after 2 weeks
    82  	expire := 14 * 24 * time.Hour
    83  	return machine.RemoveImageAfterExpire(f.CacheDir, expire)
    84  }
    85  
    86  func getFedoraDownload() (*url.URL, string, string, int64, error) {
    87  	var releaseURL string
    88  	arch := machine.DetermineMachineArch()
    89  	switch arch {
    90  	case "arm64":
    91  		releaseURL = githubArmReleaseURL
    92  	case "amd64":
    93  		releaseURL = githubX86ReleaseURL
    94  	default:
    95  		return nil, "", "", -1, fmt.Errorf("CPU architecture %q is not supported", arch)
    96  	}
    97  
    98  	downloadURL, err := url.Parse(releaseURL)
    99  	if err != nil {
   100  		return nil, "", "", -1, fmt.Errorf("invalid URL generated from discovered Fedora file: %s: %w", releaseURL, err)
   101  	}
   102  
   103  	resp, err := http.Head(releaseURL)
   104  	if err != nil {
   105  		return nil, "", "", -1, fmt.Errorf("head request failed: %s: %w", releaseURL, err)
   106  	}
   107  	_ = resp.Body.Close()
   108  	contentLen := resp.ContentLength
   109  
   110  	if resp.StatusCode != http.StatusOK {
   111  		return nil, "", "", -1, fmt.Errorf("head request failed: %s: %w", releaseURL, err)
   112  	}
   113  
   114  	verURL := *downloadURL
   115  	verURL.Path = path.Join(path.Dir(downloadURL.Path), "version")
   116  
   117  	resp, err = http.Get(verURL.String())
   118  	if err != nil {
   119  		return nil, "", "", -1, fmt.Errorf("get request failed: %s: %w", verURL.String(), err)
   120  	}
   121  
   122  	defer resp.Body.Close()
   123  	bytes, err := io.ReadAll(&io.LimitedReader{R: resp.Body, N: 1024})
   124  	if err != nil {
   125  		return nil, "", "", -1, fmt.Errorf("failed reading: %s: %w", verURL.String(), err)
   126  	}
   127  
   128  	return downloadURL, strings.TrimSpace(string(bytes)), arch, contentLen, nil
   129  }