github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/machine/fcos.go (about) 1 //go:build amd64 || arm64 2 // +build amd64 arm64 3 4 package machine 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "io/ioutil" 10 "net/http" 11 url2 "net/url" 12 "os" 13 "path/filepath" 14 "runtime" 15 "strings" 16 17 "github.com/coreos/stream-metadata-go/fedoracoreos" 18 "github.com/coreos/stream-metadata-go/release" 19 "github.com/coreos/stream-metadata-go/stream" 20 "github.com/pkg/errors" 21 22 digest "github.com/opencontainers/go-digest" 23 "github.com/sirupsen/logrus" 24 ) 25 26 // These should eventually be moved into machine/qemu as 27 // they are specific to running qemu 28 var ( 29 artifact = "qemu" 30 Format = "qcow2.xz" 31 ) 32 33 const ( 34 // Used for testing the latest podman in fcos 35 // special builds 36 podmanTesting = "podman-testing" 37 PodmanTestingHost = "fedorapeople.org" 38 PodmanTestingURL = "groups/podman/testing" 39 ) 40 41 type FcosDownload struct { 42 Download 43 } 44 45 func NewFcosDownloader(vmType, vmName, imageStream string) (DistributionDownload, error) { 46 info, err := GetFCOSDownload(imageStream) 47 if err != nil { 48 return nil, err 49 } 50 urlSplit := strings.Split(info.Location, "/") 51 imageName := urlSplit[len(urlSplit)-1] 52 url, err := url2.Parse(info.Location) 53 if err != nil { 54 return nil, err 55 } 56 57 dataDir, err := GetDataDir(vmType) 58 if err != nil { 59 return nil, err 60 } 61 62 fcd := FcosDownload{ 63 Download: Download{ 64 Arch: getFcosArch(), 65 Artifact: artifact, 66 Format: Format, 67 ImageName: imageName, 68 LocalPath: filepath.Join(dataDir, imageName), 69 Sha256sum: info.Sha256Sum, 70 URL: url, 71 VMName: vmName, 72 }, 73 } 74 fcd.Download.LocalUncompressedFile = fcd.getLocalUncompressedName() 75 return fcd, nil 76 } 77 78 func (f FcosDownload) Get() *Download { 79 return &f.Download 80 } 81 82 type FcosDownloadInfo struct { 83 CompressionType string 84 Location string 85 Release string 86 Sha256Sum string 87 } 88 89 func (f FcosDownload) HasUsableCache() (bool, error) { 90 // check the sha of the local image if it exists 91 // get the sha of the remote image 92 // == dont bother to pull 93 if _, err := os.Stat(f.LocalPath); os.IsNotExist(err) { 94 return false, nil 95 } 96 fd, err := os.Open(f.LocalPath) 97 if err != nil { 98 return false, err 99 } 100 defer func() { 101 if err := fd.Close(); err != nil { 102 logrus.Error(err) 103 } 104 }() 105 sum, err := digest.SHA256.FromReader(fd) 106 if err != nil { 107 return false, err 108 } 109 return sum.Encoded() == f.Sha256sum, nil 110 } 111 112 func getFcosArch() string { 113 var arch string 114 // TODO fill in more architectures 115 switch runtime.GOARCH { 116 case "arm64": 117 arch = "aarch64" 118 default: 119 arch = "x86_64" 120 } 121 return arch 122 } 123 124 // getStreamURL is a wrapper for the fcos.GetStream URL 125 // so that we can inject a special stream and url for 126 // testing podman before it merges into fcos builds 127 func getStreamURL(streamType string) url2.URL { 128 // For the podmanTesting stream type, we point to 129 // a custom url on fedorapeople.org 130 if streamType == podmanTesting { 131 return url2.URL{ 132 Scheme: "https", 133 Host: PodmanTestingHost, 134 Path: fmt.Sprintf("%s/%s.json", PodmanTestingURL, "podman4"), 135 } 136 } 137 return fedoracoreos.GetStreamURL(streamType) 138 } 139 140 // This should get Exported and stay put as it will apply to all fcos downloads 141 // getFCOS parses fedoraCoreOS's stream and returns the image download URL and the release version 142 func GetFCOSDownload(imageStream string) (*FcosDownloadInfo, error) { //nolint:staticcheck 143 var ( 144 fcosstable stream.Stream 145 altMeta release.Release 146 streamType string 147 ) 148 149 switch imageStream { 150 case "podman-testing": 151 streamType = "podman-testing" 152 case "testing", "": 153 streamType = fedoracoreos.StreamTesting 154 case "next": 155 streamType = fedoracoreos.StreamNext 156 case "stable": 157 streamType = fedoracoreos.StreamStable 158 default: 159 return nil, errors.Errorf("invalid stream %s: valid streams are `testing` and `stable`", imageStream) 160 } 161 streamurl := getStreamURL(streamType) 162 resp, err := http.Get(streamurl.String()) 163 if err != nil { 164 return nil, err 165 } 166 body, err := ioutil.ReadAll(resp.Body) 167 if err != nil { 168 return nil, err 169 } 170 defer func() { 171 if err := resp.Body.Close(); err != nil { 172 logrus.Error(err) 173 } 174 }() 175 if imageStream == podmanTesting { 176 if err := json.Unmarshal(body, &altMeta); err != nil { 177 return nil, err 178 } 179 180 arches, ok := altMeta.Architectures[getFcosArch()] 181 if !ok { 182 return nil, fmt.Errorf("unable to pull VM image: no targetArch in stream") 183 } 184 qcow2, ok := arches.Media.Qemu.Artifacts["qcow2.xz"] 185 if !ok { 186 return nil, fmt.Errorf("unable to pull VM image: no qcow2.xz format in stream") 187 } 188 disk := qcow2.Disk 189 190 return &FcosDownloadInfo{ 191 Location: disk.Location, 192 Sha256Sum: disk.Sha256, 193 CompressionType: "xz", 194 }, nil 195 } 196 197 if err := json.Unmarshal(body, &fcosstable); err != nil { 198 return nil, err 199 } 200 arch, ok := fcosstable.Architectures[getFcosArch()] 201 if !ok { 202 return nil, fmt.Errorf("unable to pull VM image: no targetArch in stream") 203 } 204 artifacts := arch.Artifacts 205 if artifacts == nil { 206 return nil, fmt.Errorf("unable to pull VM image: no artifact in stream") 207 } 208 qemu, ok := artifacts[artifact] 209 if !ok { 210 return nil, fmt.Errorf("unable to pull VM image: no qemu artifact in stream") 211 } 212 formats := qemu.Formats 213 if formats == nil { 214 return nil, fmt.Errorf("unable to pull VM image: no formats in stream") 215 } 216 qcow, ok := formats[Format] 217 if !ok { 218 return nil, fmt.Errorf("unable to pull VM image: no qcow2.xz format in stream") 219 } 220 disk := qcow.Disk 221 if disk == nil { 222 return nil, fmt.Errorf("unable to pull VM image: no disk in stream") 223 } 224 return &FcosDownloadInfo{ 225 Location: disk.Location, 226 Release: qemu.Release, 227 Sha256Sum: disk.Sha256, 228 CompressionType: "xz", 229 }, nil 230 }