diff --git a/pkg/fileutils/download.go b/pkg/fileutils/download.go index 7bf3fea5372..ed5ce0d17e0 100644 --- a/pkg/fileutils/download.go +++ b/pkg/fileutils/download.go @@ -5,9 +5,13 @@ package fileutils import ( "context" + "crypto/sha256" "errors" "fmt" + "io" + "os" "path" + "path/filepath" "github.com/sirupsen/logrus" @@ -18,12 +22,65 @@ import ( // ErrSkipped is returned when the downloader did not attempt to download the specified file. var ErrSkipped = errors.New("skipped to download") +func CopyFile(src, dst string) error { + source, err := os.Open(src) + if err != nil { + return err + } + defer source.Close() + if err := os.MkdirAll(filepath.Dir(dst), 0o660); err != nil { + return err + } + destination, err := os.Create(dst) + if err != nil { + return err + } + defer destination.Close() + _, err = io.Copy(destination, source) + return err +} + +func GetFileSHA256(filePath string) (string, error) { + file, err := os.Open(filePath) + if err != nil { + return "", err + } + defer file.Close() + hash := sha256.New() + if _, err := io.Copy(hash, file); err != nil { + return "", err + } + return fmt.Sprintf("sha256:%x", hash.Sum(nil)), nil +} + // DownloadFile downloads a file to the cache, optionally copying it to the destination. Returns path in cache. -func DownloadFile(ctx context.Context, dest string, f limayaml.File, decompress bool, description string, expectedArch limayaml.Arch) (string, error) { +func DownloadFile(ctx context.Context, dest string, f limayaml.File, decompress bool, description string, expectedArch limayaml.Arch) (_ string, reterr error) { if f.Arch != expectedArch { return "", fmt.Errorf("%w: %q: unsupported arch: %q", ErrSkipped, f.Location, f.Arch) } - fields := logrus.Fields{"location": f.Location, "arch": f.Arch, "digest": f.Digest} + fields := logrus.Fields{"location": f.Location, "arch": f.Arch, "digest": f.Digest, "LocalPath": f.LocalPath} + if f.LocalPath != "" { + if _, err := os.Stat(f.LocalPath); err != nil { + return "", err + } + logrus.WithFields(fields).Infof("Attempting to copy local file %s", description) + if reterr != nil { + defer os.Remove(dest) + } + if err := CopyFile(f.LocalPath, dest); err != nil { + return "", fmt.Errorf("failed to copy file: %w", err) + } + sha256Sum, err := GetFileSHA256(dest) + if err != nil { + return "", fmt.Errorf("failed to getsha256: %w", err) + } + + if sha256Sum != f.Digest.String() { + return "", fmt.Errorf("wrong sha256 for %s", dest) + } + return f.LocalPath, nil + } + logrus.WithFields(fields).Infof("Attempting to download %s", description) res, err := downloader.Download(ctx, dest, f.Location, downloader.WithCache(), diff --git a/pkg/limayaml/limayaml.go b/pkg/limayaml/limayaml.go index a21c4aa5cf0..b8731ee3e93 100644 --- a/pkg/limayaml/limayaml.go +++ b/pkg/limayaml/limayaml.go @@ -121,9 +121,10 @@ type Rosetta struct { } type File struct { - Location string `yaml:"location" json:"location"` // REQUIRED - Arch Arch `yaml:"arch,omitempty" json:"arch,omitempty"` - Digest digest.Digest `yaml:"digest,omitempty" json:"digest,omitempty"` + Location string `yaml:"location" json:"location"` // REQUIRED + Arch Arch `yaml:"arch,omitempty" json:"arch,omitempty"` + Digest digest.Digest `yaml:"digest,omitempty" json:"digest,omitempty"` + LocalPath string `yaml:"localPath,omitempty" json:"localPath,omitempty"` } type FileWithVMType struct {