github.com/apptainer/singularity@v3.1.1+incompatible/cmd/internal/cli/pull.go (about) 1 // Copyright (c) 2019, 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 cli 7 8 import ( 9 "io" 10 "os" 11 "os/signal" 12 "syscall" 13 14 "github.com/spf13/cobra" 15 "github.com/sylabs/singularity/docs" 16 "github.com/sylabs/singularity/internal/pkg/client/cache" 17 "github.com/sylabs/singularity/internal/pkg/libexec" 18 "github.com/sylabs/singularity/internal/pkg/sylog" 19 "github.com/sylabs/singularity/internal/pkg/util/uri" 20 "github.com/sylabs/singularity/pkg/build/types" 21 client "github.com/sylabs/singularity/pkg/client/library" 22 ) 23 24 const ( 25 // LibraryProtocol holds the sylabs cloud library base URI 26 // for more info refer to https://cloud.sylabs.io/library 27 LibraryProtocol = "library" 28 // ShubProtocol holds singularity hub base URI 29 // for more info refer to https://singularity-hub.org/ 30 ShubProtocol = "shub" 31 // HTTPProtocol holds the remote http base URI 32 HTTPProtocol = "http" 33 // HTTPSProtocol holds the remote https base URI 34 HTTPSProtocol = "https" 35 ) 36 37 var ( 38 // PullLibraryURI holds the base URI to a Sylabs library API instance 39 PullLibraryURI string 40 // PullImageName holds the name to be given to the pulled image 41 PullImageName string 42 ) 43 44 func init() { 45 PullCmd.Flags().SetInterspersed(false) 46 47 PullCmd.Flags().StringVar(&PullLibraryURI, "library", "https://library.sylabs.io", "the library to pull from") 48 PullCmd.Flags().SetAnnotation("library", "envkey", []string{"LIBRARY"}) 49 50 PullCmd.Flags().BoolVarP(&force, "force", "F", false, "overwrite an image file if it exists") 51 PullCmd.Flags().SetAnnotation("force", "envkey", []string{"FORCE"}) 52 53 PullCmd.Flags().StringVar(&PullImageName, "name", "", "specify a custom image name") 54 PullCmd.Flags().Lookup("name").Hidden = true 55 PullCmd.Flags().SetAnnotation("name", "envkey", []string{"NAME"}) 56 57 PullCmd.Flags().StringVar(&tmpDir, "tmpdir", "", "specify a temporary directory to use for build") 58 PullCmd.Flags().Lookup("tmpdir").Hidden = true 59 PullCmd.Flags().SetAnnotation("tmpdir", "envkey", []string{"TMPDIR"}) 60 61 PullCmd.Flags().BoolVar(&noHTTPS, "nohttps", false, "do NOT use HTTPS, for communicating with local docker registry") 62 PullCmd.Flags().SetAnnotation("nohttps", "envkey", []string{"NOHTTPS"}) 63 64 PullCmd.Flags().AddFlag(actionFlags.Lookup("docker-username")) 65 PullCmd.Flags().AddFlag(actionFlags.Lookup("docker-password")) 66 PullCmd.Flags().AddFlag(actionFlags.Lookup("docker-login")) 67 68 SingularityCmd.AddCommand(PullCmd) 69 } 70 71 // PullCmd singularity pull 72 var PullCmd = &cobra.Command{ 73 DisableFlagsInUseLine: true, 74 Args: cobra.RangeArgs(1, 2), 75 PreRun: sylabsToken, 76 Run: pullRun, 77 Use: docs.PullUse, 78 Short: docs.PullShort, 79 Long: docs.PullLong, 80 Example: docs.PullExample, 81 } 82 83 func pullRun(cmd *cobra.Command, args []string) { 84 i := len(args) - 1 // uri is stored in args[len(args)-1] 85 transport, ref := uri.Split(args[i]) 86 if ref == "" { 87 sylog.Fatalf("bad uri %s", args[i]) 88 } 89 90 var name string 91 if PullImageName == "" { 92 name = args[0] 93 if len(args) == 1 { 94 if transport == "" { 95 name = uri.GetName("library://" + args[i]) 96 } else { 97 name = uri.GetName(args[i]) // TODO: If not library/shub & no name specified, simply put to cache 98 } 99 } 100 } else { 101 name = PullImageName 102 } 103 104 // monitor for OS signals and remove invalid file 105 c := make(chan os.Signal) 106 signal.Notify(c, os.Interrupt, syscall.SIGTERM) 107 go func(fileName string) { 108 <-c 109 sylog.Debugf("Removing incomplete file because of receiving Termination signal") 110 os.Remove(fileName) 111 os.Exit(1) 112 }(name) 113 114 switch transport { 115 case LibraryProtocol, "": 116 if !force { 117 if _, err := os.Stat(name); err == nil { 118 sylog.Fatalf("image file already exists - will not overwrite") 119 } 120 } 121 122 libraryImage, err := client.GetImage(PullLibraryURI, authToken, args[i]) 123 if err != nil { 124 sylog.Fatalf("While getting image info: %v", err) 125 } 126 127 var imageName string 128 if transport == "" { 129 imageName = uri.GetName("library://" + args[i]) 130 } else { 131 imageName = uri.GetName(args[i]) 132 } 133 imagePath := cache.LibraryImage(libraryImage.Hash, imageName) 134 if exists, err := cache.LibraryImageExists(libraryImage.Hash, imageName); err != nil { 135 sylog.Fatalf("unable to check if %v exists: %v", imagePath, err) 136 } else if !exists { 137 sylog.Infof("Downloading library image") 138 if err = client.DownloadImage(imagePath, args[i], PullLibraryURI, true, authToken); err != nil { 139 sylog.Fatalf("unable to Download Image: %v", err) 140 } 141 142 if cacheFileHash, err := client.ImageHash(imagePath); err != nil { 143 sylog.Fatalf("Error getting ImageHash: %v", err) 144 } else if cacheFileHash != libraryImage.Hash { 145 sylog.Fatalf("Cached File Hash(%s) and Expected Hash(%s) does not match", cacheFileHash, libraryImage.Hash) 146 } 147 } 148 149 // Perms are 777 *prior* to umask 150 dstFile, err := os.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777) 151 if err != nil { 152 sylog.Fatalf("%v\n", err) 153 } 154 defer dstFile.Close() 155 156 srcFile, err := os.OpenFile(imagePath, os.O_RDONLY, 0444) 157 if err != nil { 158 sylog.Fatalf("%v\n", err) 159 } 160 defer srcFile.Close() 161 162 // Copy SIF from cache 163 _, err = io.Copy(dstFile, srcFile) 164 if err != nil { 165 sylog.Fatalf("%v\n", err) 166 } 167 case ShubProtocol: 168 libexec.PullShubImage(name, args[i], force, noHTTPS) 169 case HTTPProtocol, HTTPSProtocol: 170 libexec.PullNetImage(name, args[i], force) 171 default: 172 if !force { 173 if _, err := os.Stat(name); err == nil { 174 sylog.Fatalf("image file already exists - will not overwrite") 175 } 176 } 177 178 authConf, err := makeDockerCredentials(cmd) 179 if err != nil { 180 sylog.Fatalf("While creating Docker credentials: %v", err) 181 } 182 183 libexec.PullOciImage(name, args[i], types.Options{ 184 TmpDir: tmpDir, 185 Force: force, 186 NoHTTPS: noHTTPS, 187 DockerAuthConfig: authConf, 188 }) 189 } 190 }