File: api.go

package info (click to toggle)
singularity-container 4.0.3%2Bds1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 21,672 kB
  • sloc: asm: 3,857; sh: 2,125; ansic: 1,677; awk: 414; makefile: 110; python: 99
file content (111 lines) | stat: -rw-r--r-- 2,873 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// Copyright (c) 2018-2022, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package shub

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"

	"github.com/sylabs/singularity/v4/pkg/sylog"
	useragent "github.com/sylabs/singularity/v4/pkg/util/user-agent"
)

const (
	defaultRegistry string = `https://singularity-hub.org`
	shubAPIRoute    string = "/api/container/"
	// URINotSupported if we are using a non default registry error out for now
	URINotSupported string = "Only the default Singularity Hub registry is supported for now"
)

// URI stores the various components of a singularityhub URI
type URI struct {
	registry  string
	user      string
	container string
	tag       string
	digest    string
}

func (s *URI) String() string {
	return s.registry + s.user + "/" + s.container + s.tag + s.digest
}

// APIResponse holds the information returned from the Shub API
type APIResponse struct {
	Image   string `json:"image"`
	Name    string `json:"name"`
	Tag     string `json:"tag"`
	Version string `json:"version"`
	Commit  string `json:"commit"`
}

// GetManifest will return the image manifest for a container uri
// from Singularity Hub.
func GetManifest(uri URI, noHTTPS bool) (APIResponse, error) {
	// Create a new http Hub client
	httpc := http.Client{
		Timeout: 30 * time.Second,
	}

	if uri.registry != defaultRegistry+shubAPIRoute {
		uri.registry = "https://" + uri.registry
	}

	// Create the request, add headers context
	url, err := url.Parse(uri.registry + uri.user + "/" + uri.container + uri.tag + uri.digest)
	if err != nil {
		return APIResponse{}, err
	}

	if noHTTPS {
		url.Scheme = "http"
	}

	req, err := http.NewRequest(http.MethodGet, url.String(), nil)
	if err != nil {
		return APIResponse{}, err
	}
	req.Header.Set("User-Agent", useragent.Value())

	sylog.Debugf("shub request: %s", req.URL.String())

	// Do the request, if status isn't success, return error
	res, err := httpc.Do(req)
	if res == nil {
		return APIResponse{}, fmt.Errorf("no response received from singularity hub")
	}
	if res.StatusCode == http.StatusNotFound {
		return APIResponse{}, fmt.Errorf("the requested manifest was not found in singularity hub")
	}
	sylog.Debugf("%s response received, beginning manifest download\n", res.Status)

	if err != nil {
		return APIResponse{}, err
	}
	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		err = errors.New(res.Status)
		return APIResponse{}, err
	}

	body, err := io.ReadAll(res.Body)
	if err != nil {
		return APIResponse{}, err
	}

	var manifest APIResponse

	err = json.Unmarshal(body, &manifest)
	sylog.Debugf("manifest image name: %v\n", manifest.Name)

	return manifest, err
}