github.com/apptainer/singularity@v3.1.1+incompatible/pkg/client/net/pull.go (about) 1 // Copyright (c) 2018, Sylabs Inc. All rights reserved. 2 // This software is licensed under a 3-clause BSD license. Please consult the 3 // LICENSE.md file distributed with the sources of this project regarding your 4 // rights to use or distribute this software. 5 6 package client 7 8 import ( 9 "bytes" 10 "fmt" 11 "io" 12 "net/http" 13 "os" 14 "regexp" 15 "strings" 16 "time" 17 18 "github.com/sylabs/singularity/internal/pkg/sylog" 19 "github.com/sylabs/singularity/pkg/util/user-agent" 20 "gopkg.in/cheggaaa/pb.v1" 21 ) 22 23 // Timeout for an image pull in seconds - could be a large download... 24 const pullTimeout = 1800 25 26 // IsNetPullRef returns true if the provided string is a valid url 27 // reference for a pull operation. 28 func IsNetPullRef(libraryRef string) bool { 29 match, _ := regexp.MatchString("^http(s)?://", libraryRef) 30 return match 31 } 32 33 // DownloadImage will retrieve an image from the Container Library, 34 // saving it into the specified file 35 func DownloadImage(filePath string, libraryURL string, Force bool) error { 36 37 if !IsNetPullRef(libraryURL) { 38 return fmt.Errorf("Not a valid url reference: %s", libraryURL) 39 } 40 if filePath == "" { 41 refParts := strings.Split(libraryURL, "/") 42 filePath = fmt.Sprintf("%s", refParts[len(refParts)-1]) 43 sylog.Infof("Download filename not provided. Downloading to: %s\n", filePath) 44 } 45 46 url := libraryURL 47 sylog.Debugf("Pulling from URL: %s\n", url) 48 49 if !Force { 50 if _, err := os.Stat(filePath); err == nil { 51 return fmt.Errorf("image file already exists - will not overwrite") 52 } 53 } 54 55 client := &http.Client{ 56 Timeout: pullTimeout * time.Second, 57 } 58 59 req, err := http.NewRequest(http.MethodGet, url, nil) 60 if err != nil { 61 return err 62 } 63 64 req.Header.Set("User-Agent", useragent.Value()) 65 66 res, err := client.Do(req) 67 if err != nil { 68 return err 69 } 70 defer res.Body.Close() 71 72 if res.StatusCode == http.StatusNotFound { 73 return fmt.Errorf("The requested image was not found in the library") 74 } 75 76 if res.StatusCode != http.StatusOK { 77 buf := new(bytes.Buffer) 78 buf.ReadFrom(res.Body) 79 s := buf.String() 80 return fmt.Errorf("Download did not succeed: %d %s\n\t", 81 res.StatusCode, s) 82 } 83 84 sylog.Debugf("OK response received, beginning body download\n") 85 86 // Perms are 777 *prior* to umask 87 out, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777) 88 if err != nil { 89 return err 90 } 91 defer out.Close() 92 93 sylog.Debugf("Created output file: %s\n", filePath) 94 95 bodySize := res.ContentLength 96 bar := pb.New(int(bodySize)).SetUnits(pb.U_BYTES) 97 if sylog.GetLevel() < 0 { 98 bar.NotPrint = true 99 } 100 bar.ShowTimeLeft = true 101 bar.ShowSpeed = true 102 bar.Start() 103 104 // create proxy reader 105 bodyProgress := bar.NewProxyReader(res.Body) 106 107 // Write the body to file 108 _, err = io.Copy(out, bodyProgress) 109 if err != nil { 110 return err 111 } 112 113 bar.Finish() 114 115 sylog.Debugf("Download complete\n") 116 117 return nil 118 119 }