github.com/apptainer/singularity@v3.1.1+incompatible/pkg/client/library/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 "fmt" 10 "io" 11 "net/http" 12 "os" 13 "strings" 14 "time" 15 16 "github.com/sylabs/singularity/internal/pkg/sylog" 17 useragent "github.com/sylabs/singularity/pkg/util/user-agent" 18 pb "gopkg.in/cheggaaa/pb.v1" 19 ) 20 21 // Timeout for an image pull in seconds - could be a large download... 22 const pullTimeout = 1800 23 24 // DownloadImage will retrieve an image from the Container Library, 25 // saving it into the specified file 26 func DownloadImage(filePath string, libraryRef string, libraryURL string, Force bool, authToken string) error { 27 28 if !IsLibraryPullRef(libraryRef) { 29 return fmt.Errorf("Not a valid library reference: %s", libraryRef) 30 } 31 32 if filePath == "" { 33 _, _, container, tags := parseLibraryRef(libraryRef) 34 filePath = fmt.Sprintf("%s_%s.sif", container, tags[0]) 35 sylog.Infof("Download filename not provided. Downloading to: %s\n", filePath) 36 } 37 38 libraryRef = strings.TrimPrefix(libraryRef, "library://") 39 40 if strings.Index(libraryRef, ":") == -1 { 41 libraryRef += ":latest" 42 } 43 44 url := libraryURL + "/v1/imagefile/" + libraryRef 45 46 sylog.Debugf("Pulling from URL: %s\n", url) 47 48 if !Force { 49 if _, err := os.Stat(filePath); err == nil { 50 return fmt.Errorf("image file already exists - will not overwrite") 51 } 52 } 53 54 client := &http.Client{ 55 Timeout: pullTimeout * time.Second, 56 } 57 58 req, err := http.NewRequest(http.MethodGet, url, nil) 59 if err != nil { 60 return err 61 } 62 63 if authToken != "" { 64 req.Header.Set("Authorization", "Bearer "+authToken) 65 } 66 req.Header.Set("User-Agent", useragent.Value()) 67 68 res, err := client.Do(req) 69 if err != nil { 70 return err 71 } 72 defer res.Body.Close() 73 74 if res.StatusCode == http.StatusNotFound { 75 return fmt.Errorf("The requested image was not found in the library") 76 } 77 78 if res.StatusCode != http.StatusOK { 79 jRes, err := ParseErrorBody(res.Body) 80 if err != nil { 81 jRes = ParseErrorResponse(res) 82 } 83 return fmt.Errorf("Download did not succeed: %d %s\n\t%v", 84 jRes.Error.Code, jRes.Error.Status, jRes.Error.Message) 85 } 86 87 sylog.Debugf("OK response received, beginning body download\n") 88 89 // Perms are 777 *prior* to umask 90 out, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777) 91 if err != nil { 92 return err 93 } 94 defer out.Close() 95 96 sylog.Debugf("Created output file: %s\n", filePath) 97 98 bodySize := res.ContentLength 99 bar := pb.New(int(bodySize)).SetUnits(pb.U_BYTES) 100 if sylog.GetLevel() < 0 { 101 bar.NotPrint = true 102 } 103 bar.ShowTimeLeft = true 104 bar.ShowSpeed = true 105 106 // create proxy reader 107 bodyProgress := bar.NewProxyReader(res.Body) 108 109 bar.Start() 110 111 // Write the body to file 112 _, err = io.Copy(out, bodyProgress) 113 if err != nil { 114 return err 115 } 116 117 bar.Finish() 118 119 sylog.Debugf("Download complete\n") 120 121 return nil 122 123 }