github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/machine/fedora.go (about)

     1  //go:build amd64 || arm64
     2  // +build amd64 arm64
     3  
     4  package machine
     5  
     6  import (
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"net/url"
    12  	"os"
    13  	"path/filepath"
    14  	"regexp"
    15  
    16  	"github.com/pkg/errors"
    17  	"github.com/sirupsen/logrus"
    18  )
    19  
    20  const (
    21  	githubURL = "http://github.com/fedora-cloud/docker-brew-fedora/"
    22  )
    23  
    24  var fedoraxzRegex = regexp.MustCompile(`fedora[^\"]+xz`)
    25  
    26  type FedoraDownload struct {
    27  	Download
    28  }
    29  
    30  func NewFedoraDownloader(vmType, vmName, releaseStream string) (DistributionDownload, error) {
    31  	imageName, downloadURL, size, err := getFedoraDownload(releaseStream)
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  
    36  	dataDir, err := GetDataDir(vmType)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	f := FedoraDownload{
    42  		Download: Download{
    43  			Arch:      getFcosArch(),
    44  			Artifact:  artifact,
    45  			Format:    Format,
    46  			ImageName: imageName,
    47  			LocalPath: filepath.Join(dataDir, imageName),
    48  			URL:       downloadURL,
    49  			VMName:    vmName,
    50  			Size:      size,
    51  		},
    52  	}
    53  	f.Download.LocalUncompressedFile = f.getLocalUncompressedName()
    54  	return f, nil
    55  }
    56  
    57  func (f FedoraDownload) Get() *Download {
    58  	return &f.Download
    59  }
    60  
    61  func (f FedoraDownload) HasUsableCache() (bool, error) {
    62  	info, err := os.Stat(f.LocalPath)
    63  	if err != nil {
    64  		if errors.Is(err, os.ErrNotExist) {
    65  			return false, nil
    66  		}
    67  		return false, err
    68  	}
    69  	return info.Size() == f.Size, nil
    70  }
    71  
    72  func truncRead(url string) ([]byte, error) {
    73  	resp, err := http.Get(url)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	defer func() {
    79  		if err := resp.Body.Close(); err != nil {
    80  			logrus.Error(err)
    81  		}
    82  	}()
    83  
    84  	body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 10*1024*1024))
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	_, _ = io.Copy(io.Discard, resp.Body)
    90  
    91  	return body, nil
    92  }
    93  
    94  func getFedoraDownload(releaseStream string) (string, *url.URL, int64, error) {
    95  	dirURL := githubURL + "tree/" + releaseStream + "/" + getFcosArch() + "/"
    96  	body, err := truncRead(dirURL)
    97  	if err != nil {
    98  		return "", nil, -1, err
    99  	}
   100  
   101  	file := fedoraxzRegex.FindString(string(body))
   102  	if len(file) == 0 {
   103  		return "", nil, -1, fmt.Errorf("could not locate Fedora download at %s", dirURL)
   104  	}
   105  
   106  	rawURL := githubURL + "raw/" + releaseStream + "/" + getFcosArch() + "/"
   107  	newLocation := rawURL + file
   108  	downloadURL, err := url.Parse(newLocation)
   109  	if err != nil {
   110  		return "", nil, -1, errors.Wrapf(err, "invalid URL generated from discovered Fedora file: %s", newLocation)
   111  	}
   112  
   113  	resp, err := http.Head(newLocation)
   114  	if err != nil {
   115  		return "", nil, -1, errors.Wrapf(err, "head request failed: %s", newLocation)
   116  	}
   117  	_ = resp.Body.Close()
   118  
   119  	if resp.StatusCode != http.StatusOK {
   120  		return "", nil, -1, fmt.Errorf("head request failed [%d] on download: %s", resp.StatusCode, newLocation)
   121  	}
   122  
   123  	return file, downloadURL, resp.ContentLength, nil
   124  }