podman

Форк
0
/
pull.go 
234 строки · 6.0 Кб
1
//go:build amd64 || arm64
2

3
package machine
4

5
import (
6
	"errors"
7
	"fmt"
8
	"io"
9
	"net/http"
10
	url2 "net/url"
11
	"os"
12
	"path/filepath"
13
	"strings"
14
	"time"
15

16
	"github.com/containers/podman/v5/pkg/machine/compression"
17
	"github.com/containers/podman/v5/pkg/machine/define"
18
	"github.com/containers/podman/v5/pkg/machine/env"
19
	"github.com/containers/podman/v5/pkg/machine/ocipull"
20
	"github.com/containers/podman/v5/utils"
21
	"github.com/sirupsen/logrus"
22
)
23

24
// GenericDownload is used when a user provides a URL
25
// or path for an image
26
type GenericDownload struct {
27
	Download
28
}
29

30
// NewGenericDownloader is used when the disk image is provided by the user
31
func NewGenericDownloader(vmType define.VMType, vmName, pullPath string) (DistributionDownload, error) {
32
	var (
33
		imageName string
34
	)
35
	dataDir, err := env.GetDataDir(vmType)
36
	if err != nil {
37
		return nil, err
38
	}
39
	cacheDir, err := env.GetCacheDir(vmType)
40
	if err != nil {
41
		return nil, err
42
	}
43
	dl := Download{}
44
	// Is pullpath a file or url?
45
	if getURL := supportedURL(pullPath); getURL != nil {
46
		urlSplit := strings.Split(getURL.Path, "/")
47
		imageName = urlSplit[len(urlSplit)-1]
48
		dl.URL = getURL
49
		dl.LocalPath = filepath.Join(cacheDir, imageName)
50
	} else {
51
		// Dealing with FilePath
52
		imageName = filepath.Base(pullPath)
53
		dl.LocalPath = pullPath
54
	}
55
	dl.VMName = vmName
56
	dl.ImageName = imageName
57
	dl.LocalUncompressedFile = dl.GetLocalUncompressedFile(dataDir)
58
	// The download needs to be pulled into the datadir
59

60
	gd := GenericDownload{Download: dl}
61
	return gd, nil
62
}
63

64
func supportedURL(path string) (url *url2.URL) {
65
	getURL, err := url2.Parse(path)
66
	if err != nil {
67
		// ignore error, probably not a URL, fallback & treat as file path
68
		return nil
69
	}
70

71
	// Check supported scheme. Since URL is passed to net.http, only http
72
	// schemes are supported. Also, windows drive paths can resemble a
73
	// URL, but with a single letter scheme. These values should be
74
	// passed through for interpretation as a file path.
75
	switch getURL.Scheme {
76
	case "http":
77
		fallthrough
78
	case "https":
79
		return getURL
80
	default:
81
		return nil
82
	}
83
}
84

85
func (dl Download) GetLocalUncompressedFile(dataDir string) string {
86
	compressedFilename := dl.VMName + "_" + dl.ImageName
87
	extension := compression.KindFromFile(compressedFilename)
88
	uncompressedFile := strings.TrimSuffix(compressedFilename, fmt.Sprintf(".%s", extension.String()))
89
	dl.LocalUncompressedFile = filepath.Join(dataDir, uncompressedFile)
90
	return dl.LocalUncompressedFile
91
}
92

93
func (g GenericDownload) Get() *Download {
94
	return &g.Download
95
}
96

97
func (g GenericDownload) HasUsableCache() (bool, error) {
98
	// If we have a URL for this "downloader", we now pull it
99
	return g.URL == nil, nil
100
}
101

102
// CleanCache cleans out downloaded uncompressed image files
103
func (g GenericDownload) CleanCache() error {
104
	// Remove any image that has been downloaded via URL
105
	// We never read from cache for generic downloads
106
	if g.URL != nil {
107
		if err := os.Remove(g.LocalPath); err != nil && !errors.Is(err, os.ErrNotExist) {
108
			return err
109
		}
110
	}
111
	return nil
112
}
113

114
func DownloadImage(d DistributionDownload) error {
115
	// check if the latest image is already present
116
	ok, err := d.HasUsableCache()
117
	if err != nil {
118
		return err
119
	}
120
	if !ok {
121
		if err := DownloadVMImage(d.Get().URL, d.Get().ImageName, d.Get().LocalPath); err != nil {
122
			return err
123
		}
124
		// Clean out old cached images, since we didn't find needed image in cache
125
		defer func() {
126
			if err = d.CleanCache(); err != nil {
127
				logrus.Warnf("error cleaning machine image cache: %s", err)
128
			}
129
		}()
130
	}
131
	localPath, err := define.NewMachineFile(d.Get().LocalPath, nil)
132
	if err != nil {
133
		return err
134
	}
135
	return compression.Decompress(localPath, d.Get().LocalUncompressedFile)
136
}
137

138
// DownloadVMImage downloads a VM image from url to given path
139
// with download status
140
func DownloadVMImage(downloadURL *url2.URL, imageName string, localImagePath string) error {
141
	out, err := os.Create(localImagePath)
142
	if err != nil {
143
		return err
144
	}
145
	defer func() {
146
		if err := out.Close(); err != nil {
147
			logrus.Error(err)
148
		}
149
	}()
150

151
	resp, err := http.Get(downloadURL.String())
152
	if err != nil {
153
		return err
154
	}
155
	defer func() {
156
		if err := resp.Body.Close(); err != nil {
157
			logrus.Error(err)
158
		}
159
	}()
160

161
	if resp.StatusCode != http.StatusOK {
162
		return fmt.Errorf("downloading VM image %s: %s", downloadURL, resp.Status)
163
	}
164
	size := resp.ContentLength
165
	prefix := "Downloading VM image: " + imageName
166
	onComplete := prefix + ": done"
167

168
	p, bar := utils.ProgressBar(prefix, size, onComplete)
169

170
	proxyReader := bar.ProxyReader(resp.Body)
171
	defer func() {
172
		if err := proxyReader.Close(); err != nil {
173
			logrus.Error(err)
174
		}
175
	}()
176

177
	if _, err := io.Copy(out, proxyReader); err != nil {
178
		return err
179
	}
180

181
	p.Wait()
182
	return nil
183
}
184

185
func RemoveImageAfterExpire(dir string, expire time.Duration) error {
186
	now := time.Now()
187
	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
188
		// Delete any cache files that are older than expiry date
189
		if !info.IsDir() && (now.Sub(info.ModTime()) > expire) {
190
			err := os.Remove(path)
191
			if err != nil && !errors.Is(err, os.ErrNotExist) {
192
				logrus.Warnf("unable to clean up cached image: %s", path)
193
			} else {
194
				logrus.Debugf("cleaning up cached image: %s", path)
195
			}
196
		}
197
		return nil
198
	})
199
	return err
200
}
201

202
// AcquireAlternateImage downloads the alternate image the user provided, which
203
// can be a file path or URL
204
func (dl Download) AcquireAlternateImage(inputPath string) (*define.VMFile, error) {
205
	g, err := NewGenericDownloader(dl.VMKind, dl.VMName, inputPath)
206
	if err != nil {
207
		return nil, err
208
	}
209

210
	imagePath, err := define.NewMachineFile(g.Get().LocalUncompressedFile, nil)
211
	if err != nil {
212
		return nil, err
213
	}
214

215
	if err := DownloadImage(g); err != nil {
216
		return nil, err
217
	}
218

219
	return imagePath, nil
220
}
221

222
func isOci(input string) (bool, *ocipull.OCIKind, error) { //nolint:unused
223
	inputURL, err := url2.Parse(input)
224
	if err != nil {
225
		return false, nil, err
226
	}
227
	switch inputURL.Scheme {
228
	case ocipull.OCIDir.String():
229
		return true, &ocipull.OCIDir, nil
230
	case ocipull.OCIRegistry.String():
231
		return true, &ocipull.OCIRegistry, nil
232
	}
233
	return false, nil, nil
234
}
235

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.