2016-08-13 01:23:13 -04:00
|
|
|
package registry
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
2017-04-04 10:12:36 -04:00
|
|
|
"regexp"
|
2016-08-13 01:23:13 -04:00
|
|
|
"strings"
|
2018-06-06 13:00:42 -04:00
|
|
|
"time"
|
2016-08-13 01:23:13 -04:00
|
|
|
|
2017-12-11 17:33:26 -05:00
|
|
|
"github.com/docker/distribution/manifest/manifestlist"
|
|
|
|
"github.com/docker/distribution/manifest/schema2"
|
2017-03-05 01:10:04 -05:00
|
|
|
"github.com/docker/docker/api/types"
|
2016-08-13 01:23:13 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// Registry defines the client for retriving information from the registry API.
|
|
|
|
type Registry struct {
|
2017-02-12 21:45:47 -05:00
|
|
|
URL string
|
|
|
|
Domain string
|
|
|
|
Username string
|
|
|
|
Password string
|
|
|
|
Client *http.Client
|
|
|
|
Logf LogfCallback
|
2018-06-06 15:43:12 -04:00
|
|
|
Opt Opt
|
2016-08-13 01:23:13 -04:00
|
|
|
}
|
|
|
|
|
2017-04-04 10:12:36 -04:00
|
|
|
var reProtocol = regexp.MustCompile("^https?://")
|
|
|
|
|
2016-08-13 01:23:13 -04:00
|
|
|
// 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...)
|
|
|
|
}
|
|
|
|
|
2018-06-06 12:49:29 -04:00
|
|
|
// Opt holds the options for a new registry.
|
|
|
|
type Opt struct {
|
|
|
|
Insecure bool
|
|
|
|
Debug bool
|
|
|
|
SkipPing bool
|
2018-06-06 13:00:42 -04:00
|
|
|
Timeout time.Duration
|
2018-06-06 12:49:29 -04:00
|
|
|
}
|
|
|
|
|
2016-08-13 01:23:13 -04:00
|
|
|
// New creates a new Registry struct with the given URL and credentials.
|
2018-06-06 12:49:29 -04:00
|
|
|
func New(auth types.AuthConfig, opt Opt) (*Registry, error) {
|
2017-02-13 14:42:33 -05:00
|
|
|
transport := http.DefaultTransport
|
2016-08-13 01:23:13 -04:00
|
|
|
|
2018-06-06 12:49:29 -04:00
|
|
|
if opt.Insecure {
|
|
|
|
transport = &http.Transport{
|
|
|
|
TLSClientConfig: &tls.Config{
|
|
|
|
InsecureSkipVerify: true,
|
|
|
|
},
|
|
|
|
}
|
2016-08-13 01:23:13 -04:00
|
|
|
}
|
|
|
|
|
2018-06-06 12:49:29 -04:00
|
|
|
return newFromTransport(auth, transport, opt)
|
2016-08-13 01:23:13 -04:00
|
|
|
}
|
|
|
|
|
2018-06-06 12:49:29 -04:00
|
|
|
func newFromTransport(auth types.AuthConfig, transport http.RoundTripper, opt Opt) (*Registry, error) {
|
2017-04-04 10:12:36 -04:00
|
|
|
url := strings.TrimSuffix(auth.ServerAddress, "/")
|
|
|
|
|
|
|
|
if !reProtocol.MatchString(url) {
|
|
|
|
url = "https://" + url
|
|
|
|
}
|
|
|
|
|
2016-09-05 22:44:19 -04:00
|
|
|
tokenTransport := &TokenTransport{
|
|
|
|
Transport: transport,
|
|
|
|
Username: auth.Username,
|
|
|
|
Password: auth.Password,
|
|
|
|
}
|
|
|
|
basicAuthTransport := &BasicTransport{
|
|
|
|
Transport: tokenTransport,
|
2017-04-18 13:32:28 -04:00
|
|
|
URL: url,
|
2016-09-05 22:44:19 -04:00
|
|
|
Username: auth.Username,
|
|
|
|
Password: auth.Password,
|
|
|
|
}
|
|
|
|
errorTransport := &ErrorTransport{
|
|
|
|
Transport: basicAuthTransport,
|
|
|
|
}
|
2016-08-13 01:23:13 -04:00
|
|
|
|
|
|
|
// set the logging
|
|
|
|
logf := Quiet
|
2018-06-06 12:49:29 -04:00
|
|
|
if opt.Debug {
|
2016-08-13 01:23:13 -04:00
|
|
|
logf = Log
|
|
|
|
}
|
|
|
|
|
|
|
|
registry := &Registry{
|
2016-12-19 22:34:37 -05:00
|
|
|
URL: url,
|
2017-04-04 10:12:36 -04:00
|
|
|
Domain: reProtocol.ReplaceAllString(url, ""),
|
2016-08-13 01:23:13 -04:00
|
|
|
Client: &http.Client{
|
2018-06-06 13:00:42 -04:00
|
|
|
Timeout: opt.Timeout,
|
2016-09-05 22:44:19 -04:00
|
|
|
Transport: errorTransport,
|
2016-08-13 01:23:13 -04:00
|
|
|
},
|
2017-02-12 21:45:47 -05:00
|
|
|
Username: auth.Username,
|
|
|
|
Password: auth.Password,
|
|
|
|
Logf: logf,
|
2018-06-06 15:43:12 -04:00
|
|
|
Opt: opt,
|
2016-08-13 01:23:13 -04:00
|
|
|
}
|
|
|
|
|
2018-06-06 12:49:29 -04:00
|
|
|
if !opt.SkipPing {
|
2018-01-17 11:52:39 -05:00
|
|
|
if err := registry.Ping(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-08-13 01:23:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return registry, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2017-03-01 18:39:18 -05:00
|
|
|
func (r *Registry) getJSON(url string, response interface{}, addV2Header bool) (http.Header, error) {
|
|
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if addV2Header {
|
2017-12-11 17:33:26 -05:00
|
|
|
req.Header.Add("Accept", schema2.MediaTypeManifest)
|
|
|
|
req.Header.Add("Accept", manifestlist.MediaTypeManifestList)
|
2017-03-01 18:39:18 -05:00
|
|
|
}
|
|
|
|
resp, err := r.Client.Do(req)
|
2016-08-13 01:23:13 -04:00
|
|
|
if err != nil {
|
2016-09-20 21:16:34 -04:00
|
|
|
return nil, err
|
2016-08-13 01:23:13 -04:00
|
|
|
}
|
2016-09-05 22:44:19 -04:00
|
|
|
defer resp.Body.Close()
|
2018-02-18 11:17:29 -05:00
|
|
|
r.Logf("registry.registry resp.Status=%s", resp.Status)
|
2016-08-13 01:23:13 -04:00
|
|
|
|
2016-09-05 22:44:19 -04:00
|
|
|
if err := json.NewDecoder(resp.Body).Decode(response); err != nil {
|
2016-09-20 21:16:34 -04:00
|
|
|
return nil, err
|
2016-08-13 01:23:13 -04:00
|
|
|
}
|
|
|
|
|
2016-09-20 21:16:34 -04:00
|
|
|
return resp.Header, nil
|
2016-08-13 01:23:13 -04:00
|
|
|
}
|