/
registry.go
144 lines (124 loc) · 3.41 KB
/
registry.go
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package registry
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"regexp"
"strings"
"time"
"github.com/docker/distribution/manifest/manifestlist"
"github.com/docker/distribution/manifest/schema2"
"github.com/docker/docker/api/types"
)
// Registry defines the client for retrieving information from the registry API.
type Registry struct {
URL string
Domain string
Username string
Password string
Client *http.Client
Logf LogfCallback
Opt Opt
}
var reProtocol = regexp.MustCompile("^https?://")
// LogfCallback is the callback for formatting logs.
type LogfCallback func(format string, args ...interface{})
// Quiet discards logs silently.
func Quiet(format string, args ...interface{}) {}
// Log passes log messages to the logging package.
func Log(format string, args ...interface{}) {
log.Printf(format, args...)
}
// Opt holds the options for a new registry.
type Opt struct {
Domain string
NonSSL bool
Timeout time.Duration
Headers map[string]string
}
// New creates a new Registry struct with the given URL and credentials.
func New(ctx context.Context, auth types.AuthConfig, opt Opt) (*Registry, error) {
transport := http.DefaultTransport
return newFromTransport(ctx, auth, transport, opt)
}
func newFromTransport(ctx context.Context, auth types.AuthConfig, transport http.RoundTripper, opt Opt) (*Registry, error) {
if len(opt.Domain) < 1 {
opt.Domain = auth.ServerAddress
}
url := strings.TrimSuffix(opt.Domain, "/")
authURL := strings.TrimSuffix(auth.ServerAddress, "/")
if !reProtocol.MatchString(url) {
if !opt.NonSSL {
url = "https://" + url
} else {
url = "http://" + url
}
}
if !reProtocol.MatchString(authURL) {
if !opt.NonSSL {
authURL = "https://" + authURL
} else {
authURL = "http://" + authURL
}
}
tokenTransport := &TokenTransport{
Transport: transport,
Username: auth.Username,
Password: auth.Password,
}
basicAuthTransport := &BasicTransport{
Transport: tokenTransport,
URL: authURL,
Username: auth.Username,
Password: auth.Password,
}
errorTransport := &ErrorTransport{
Transport: basicAuthTransport,
}
// set the logging
logf := Quiet
registry := &Registry{
URL: url,
Domain: reProtocol.ReplaceAllString(url, ""),
Client: &http.Client{
Timeout: opt.Timeout,
Transport: errorTransport,
},
Username: auth.Username,
Password: auth.Password,
Logf: logf,
Opt: opt,
}
return registry, nil
}
// Url : url returns a registry URL with the passed arguements concatenated.
func (r *Registry) Url(pathTemplate string, args ...interface{}) string {
pathSuffix := fmt.Sprintf(pathTemplate, args...)
url := fmt.Sprintf("%s%s", r.URL, pathSuffix)
return url
}
// GetJSON
func (r *Registry) GetJSON(ctx context.Context, url string, response interface{}) (http.Header, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
switch response.(type) {
case *schema2.Manifest:
req.Header.Add("Accept", fmt.Sprintf("%s;q=0.9", schema2.MediaTypeManifest))
case *manifestlist.ManifestList:
req.Header.Add("Accept", fmt.Sprintf("%s;q=0.9", manifestlist.MediaTypeManifestList))
}
resp, err := r.Client.Do(req.WithContext(ctx))
if err != nil {
return nil, err
}
defer resp.Body.Close()
r.Logf("registry.registry resp.Status=%s", resp.Status)
if err := json.NewDecoder(resp.Body).Decode(response); err != nil {
return nil, err
}
return resp.Header, nil
}