164 lines
3.7 KiB
Go
164 lines
3.7 KiB
Go
package registries
|
|
|
|
import (
|
|
"compress/gzip"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/docker/docker/api/types"
|
|
"io"
|
|
"io/ioutil"
|
|
log "k8s.io/klog"
|
|
"net/http"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
// DefaultDockerRegistry is the default docker registry address.
|
|
DefaultDockerRegistry = "https://registry-1.docker.io"
|
|
|
|
DefaultDockerHub = "docker.io"
|
|
|
|
DefaultTimeout = 30 * time.Second
|
|
)
|
|
|
|
var (
|
|
bearerRegex = regexp.MustCompile(
|
|
`^\s*Bearer\s+(.*)$`)
|
|
basicRegex = regexp.MustCompile(`^\s*Basic\s+.*$`)
|
|
|
|
// ErrBasicAuth indicates that the repository requires basic rather than token authentication.
|
|
ErrBasicAuth = errors.New("basic auth required")
|
|
|
|
gcrMatcher = regexp.MustCompile(`https://([a-z]+\.|)gcr\.io/`)
|
|
)
|
|
|
|
// 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
|
|
Opt RegistryOpt
|
|
}
|
|
|
|
// Opt holds the options for a new registry.
|
|
type RegistryOpt struct {
|
|
Domain string
|
|
Timeout time.Duration
|
|
Headers map[string]string
|
|
UseSSL bool
|
|
}
|
|
|
|
type authToken struct {
|
|
Token string `json:"token"`
|
|
AccessToken string `json:"access_token"`
|
|
}
|
|
|
|
type authService struct {
|
|
Realm *url.URL
|
|
Service string
|
|
Scope []string
|
|
}
|
|
|
|
func CreateRegistryClient(username, password, domain string, useSSL bool) (*Registry, error) {
|
|
authDomain := domain
|
|
auth, err := GetAuthConfig(username, password, authDomain)
|
|
if err != nil {
|
|
log.Error(err)
|
|
return nil, err
|
|
}
|
|
|
|
// Create the registry client.
|
|
return New(auth, RegistryOpt{
|
|
Domain: domain,
|
|
UseSSL: useSSL,
|
|
})
|
|
}
|
|
|
|
// GetAuthConfig returns the docker registry AuthConfig.
|
|
func GetAuthConfig(username, password, registry string) (types.AuthConfig, error) {
|
|
registry = setDefaultRegistry(registry)
|
|
if username != "" && password != "" {
|
|
return types.AuthConfig{
|
|
Username: username,
|
|
Password: password,
|
|
ServerAddress: registry,
|
|
}, nil
|
|
}
|
|
|
|
return types.AuthConfig{
|
|
ServerAddress: registry,
|
|
}, nil
|
|
|
|
}
|
|
|
|
func setDefaultRegistry(serverAddress string) string {
|
|
if serverAddress == DefaultDockerHub || serverAddress == "" {
|
|
serverAddress = DefaultDockerRegistry
|
|
}
|
|
|
|
return serverAddress
|
|
}
|
|
|
|
func newFromTransport(auth types.AuthConfig, opt RegistryOpt) (*Registry, error) {
|
|
if len(opt.Domain) < 1 || opt.Domain == DefaultDockerHub {
|
|
opt.Domain = auth.ServerAddress
|
|
}
|
|
registryUrl := strings.TrimSuffix(opt.Domain, "/")
|
|
|
|
if !strings.HasPrefix(registryUrl, "http://") && !strings.HasPrefix(registryUrl, "https://") {
|
|
if opt.UseSSL {
|
|
registryUrl = "https://" + registryUrl
|
|
} else {
|
|
registryUrl = "http://" + registryUrl
|
|
}
|
|
}
|
|
|
|
registryURL, _ := url.Parse(registryUrl)
|
|
registry := &Registry{
|
|
URL: registryURL.String(),
|
|
Domain: registryURL.Host,
|
|
Client: &http.Client{
|
|
Timeout: DefaultTimeout,
|
|
},
|
|
Username: auth.Username,
|
|
Password: auth.Password,
|
|
Opt: opt,
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// New creates a new Registry struct with the given URL and credentials.
|
|
func New(auth types.AuthConfig, opt RegistryOpt) (*Registry, error) {
|
|
|
|
return newFromTransport(auth, opt)
|
|
}
|
|
|
|
// Decompress response.body.
|
|
func GetRespBody(resp *http.Response) ([]byte, error) {
|
|
var reader io.ReadCloser
|
|
if resp.Header.Get("Content-Encoding") == "gzip" {
|
|
reader, _ = gzip.NewReader(resp.Body)
|
|
} else {
|
|
reader = resp.Body
|
|
}
|
|
resBody, err := ioutil.ReadAll(reader)
|
|
if err != nil {
|
|
log.Error(err)
|
|
return nil, err
|
|
}
|
|
return resBody, err
|
|
}
|