github.com/apptainer/singularity@v3.1.1+incompatible/pkg/client/shub/api.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 "encoding/json" 10 "errors" 11 "fmt" 12 "io/ioutil" 13 "net/http" 14 "net/url" 15 "time" 16 17 "github.com/sylabs/singularity/internal/pkg/sylog" 18 "github.com/sylabs/singularity/pkg/util/user-agent" 19 ) 20 21 const ( 22 defaultRegistry string = `https://singularity-hub.org` 23 shubAPIRoute string = "/api/container/" 24 // URINotSupported if we are using a non default registry error out for now 25 URINotSupported string = "Only the default Singularity Hub registry is suported for now" 26 ) 27 28 // ShubURI stores the various components of a singularityhub URI 29 type ShubURI struct { 30 registry string 31 user string 32 container string 33 tag string 34 digest string 35 } 36 37 func (s *ShubURI) String() string { 38 return s.registry + s.user + "/" + s.container + s.tag + s.digest 39 } 40 41 // ShubAPIResponse holds the information returned from the Shub API 42 type ShubAPIResponse struct { 43 Image string `json:"image"` 44 Name string `json:"name"` 45 Tag string `json:"tag"` 46 Version string `json:"version"` 47 } 48 49 // getManifest will return the image manifest for a container uri 50 // from Singularity Hub. 51 func getManifest(uri ShubURI, noHTTPS bool) (manifest ShubAPIResponse, err error) { 52 53 // Create a new http Hub client 54 httpc := http.Client{ 55 Timeout: 30 * time.Second, 56 } 57 58 if uri.registry != defaultRegistry+shubAPIRoute { 59 uri.registry = "https://" + uri.registry 60 } 61 62 // Create the request, add headers context 63 url, err := url.Parse(uri.registry + uri.user + "/" + uri.container + uri.tag + uri.digest) 64 if err != nil { 65 return ShubAPIResponse{}, err 66 } 67 68 if noHTTPS { 69 url.Scheme = "http" 70 } 71 72 req, err := http.NewRequest(http.MethodGet, url.String(), nil) 73 if err != nil { 74 return ShubAPIResponse{}, err 75 } 76 req.Header.Set("User-Agent", useragent.Value()) 77 78 sylog.Debugf("shub request: %s", req.URL.String()) 79 80 // Do the request, if status isn't success, return error 81 res, err := httpc.Do(req) 82 if res == nil { 83 return ShubAPIResponse{}, fmt.Errorf("No response received from singularity hub") 84 } 85 if res.StatusCode == http.StatusNotFound { 86 return ShubAPIResponse{}, fmt.Errorf("The requested manifest was not found in singularity hub") 87 } 88 sylog.Debugf("%s response received, beginning manifest download\n", res.Status) 89 90 if err != nil { 91 return ShubAPIResponse{}, err 92 } 93 defer res.Body.Close() 94 95 if res.StatusCode != http.StatusOK { 96 err = errors.New(res.Status) 97 return ShubAPIResponse{}, err 98 } 99 100 body, err := ioutil.ReadAll(res.Body) 101 if err != nil { 102 return ShubAPIResponse{}, err 103 } 104 105 err = json.Unmarshal(body, &manifest) 106 sylog.Debugf("manifest image name: %v\n", manifest.Name) 107 if err != nil { 108 return ShubAPIResponse{}, err 109 } 110 111 return 112 }