mirror of
https://github.com/mudler/luet.git
synced 2025-09-03 16:25:19 +00:00
Switch to go mod
This commit is contained in:
26
vendor/github.com/heroku/docker-registry-client/LICENSE.md
generated
vendored
Normal file
26
vendor/github.com/heroku/docker-registry-client/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
Copyright (c) 2015, Salesforce.com, Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Salesforce.com nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
150
vendor/github.com/heroku/docker-registry-client/registry/authchallenge.go
generated
vendored
Normal file
150
vendor/github.com/heroku/docker-registry-client/registry/authchallenge.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Octet types from RFC 2616.
|
||||
type octetType byte
|
||||
|
||||
// AuthorizationChallenge carries information
|
||||
// from a WWW-Authenticate response header.
|
||||
type AuthorizationChallenge struct {
|
||||
Scheme string
|
||||
Parameters map[string]string
|
||||
}
|
||||
|
||||
var octetTypes [256]octetType
|
||||
|
||||
const (
|
||||
isToken octetType = 1 << iota
|
||||
isSpace
|
||||
)
|
||||
|
||||
func init() {
|
||||
// OCTET = <any 8-bit sequence of data>
|
||||
// CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
// CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
|
||||
// CR = <US-ASCII CR, carriage return (13)>
|
||||
// LF = <US-ASCII LF, linefeed (10)>
|
||||
// SP = <US-ASCII SP, space (32)>
|
||||
// HT = <US-ASCII HT, horizontal-tab (9)>
|
||||
// <"> = <US-ASCII double-quote mark (34)>
|
||||
// CRLF = CR LF
|
||||
// LWS = [CRLF] 1*( SP | HT )
|
||||
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||
// separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
|
||||
// | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
|
||||
// token = 1*<any CHAR except CTLs or separators>
|
||||
// qdtext = <any TEXT except <">>
|
||||
|
||||
for c := 0; c < 256; c++ {
|
||||
var t octetType
|
||||
isCtl := c <= 31 || c == 127
|
||||
isChar := 0 <= c && c <= 127
|
||||
isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0
|
||||
if strings.IndexRune(" \t\r\n", rune(c)) >= 0 {
|
||||
t |= isSpace
|
||||
}
|
||||
if isChar && !isCtl && !isSeparator {
|
||||
t |= isToken
|
||||
}
|
||||
octetTypes[c] = t
|
||||
}
|
||||
}
|
||||
|
||||
func parseAuthHeader(header http.Header) []*AuthorizationChallenge {
|
||||
var challenges []*AuthorizationChallenge
|
||||
for _, h := range header[http.CanonicalHeaderKey("WWW-Authenticate")] {
|
||||
v, p := parseValueAndParams(h)
|
||||
if v != "" {
|
||||
challenges = append(challenges, &AuthorizationChallenge{Scheme: v, Parameters: p})
|
||||
}
|
||||
}
|
||||
return challenges
|
||||
}
|
||||
|
||||
func parseValueAndParams(header string) (value string, params map[string]string) {
|
||||
params = make(map[string]string)
|
||||
value, s := expectToken(header)
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
value = strings.ToLower(value)
|
||||
s = "," + skipSpace(s)
|
||||
for strings.HasPrefix(s, ",") {
|
||||
var pkey string
|
||||
pkey, s = expectToken(skipSpace(s[1:]))
|
||||
if pkey == "" {
|
||||
return
|
||||
}
|
||||
if !strings.HasPrefix(s, "=") {
|
||||
return
|
||||
}
|
||||
var pvalue string
|
||||
pvalue, s = expectTokenOrQuoted(s[1:])
|
||||
if pvalue == "" {
|
||||
return
|
||||
}
|
||||
pkey = strings.ToLower(pkey)
|
||||
params[pkey] = pvalue
|
||||
s = skipSpace(s)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func skipSpace(s string) (rest string) {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
if octetTypes[s[i]]&isSpace == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return s[i:]
|
||||
}
|
||||
|
||||
func expectToken(s string) (token, rest string) {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
if octetTypes[s[i]]&isToken == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return s[:i], s[i:]
|
||||
}
|
||||
|
||||
func expectTokenOrQuoted(s string) (value string, rest string) {
|
||||
if !strings.HasPrefix(s, "\"") {
|
||||
return expectToken(s)
|
||||
}
|
||||
s = s[1:]
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch s[i] {
|
||||
case '"':
|
||||
return s[:i], s[i+1:]
|
||||
case '\\':
|
||||
p := make([]byte, len(s)-1)
|
||||
j := copy(p, s[:i])
|
||||
escape := true
|
||||
for i = i + i; i < len(s); i++ {
|
||||
b := s[i]
|
||||
switch {
|
||||
case escape:
|
||||
escape = false
|
||||
p[j] = b
|
||||
j++
|
||||
case b == '\\':
|
||||
escape = true
|
||||
case b == '"':
|
||||
return string(p[:j]), s[i+1:]
|
||||
default:
|
||||
p[j] = b
|
||||
j++
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
23
vendor/github.com/heroku/docker-registry-client/registry/basictransport.go
generated
vendored
Normal file
23
vendor/github.com/heroku/docker-registry-client/registry/basictransport.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type BasicTransport struct {
|
||||
Transport http.RoundTripper
|
||||
URL string
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func (t *BasicTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if strings.HasPrefix(req.URL.String(), t.URL) {
|
||||
if t.Username != "" || t.Password != "" {
|
||||
req.SetBasicAuth(t.Username, t.Password)
|
||||
}
|
||||
}
|
||||
resp, err := t.Transport.RoundTrip(req)
|
||||
return resp, err
|
||||
}
|
108
vendor/github.com/heroku/docker-registry-client/registry/blob.go
generated
vendored
Normal file
108
vendor/github.com/heroku/docker-registry-client/registry/blob.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
func (registry *Registry) DownloadBlob(repository string, digest digest.Digest) (io.ReadCloser, error) {
|
||||
url := registry.url("/v2/%s/blobs/%s", repository, digest)
|
||||
registry.Logf("registry.blob.download url=%s repository=%s digest=%s", url, repository, digest)
|
||||
|
||||
resp, err := registry.Client.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Body, nil
|
||||
}
|
||||
|
||||
func (registry *Registry) UploadBlob(repository string, digest digest.Digest, content io.Reader) error {
|
||||
uploadUrl, err := registry.initiateUpload(repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
q := uploadUrl.Query()
|
||||
q.Set("digest", digest.String())
|
||||
uploadUrl.RawQuery = q.Encode()
|
||||
|
||||
registry.Logf("registry.blob.upload url=%s repository=%s digest=%s", uploadUrl, repository, digest)
|
||||
|
||||
upload, err := http.NewRequest("PUT", uploadUrl.String(), content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
upload.Header.Set("Content-Type", "application/octet-stream")
|
||||
|
||||
_, err = registry.Client.Do(upload)
|
||||
return err
|
||||
}
|
||||
|
||||
func (registry *Registry) HasBlob(repository string, digest digest.Digest) (bool, error) {
|
||||
checkUrl := registry.url("/v2/%s/blobs/%s", repository, digest)
|
||||
registry.Logf("registry.blob.check url=%s repository=%s digest=%s", checkUrl, repository, digest)
|
||||
|
||||
resp, err := registry.Client.Head(checkUrl)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if err == nil {
|
||||
return resp.StatusCode == http.StatusOK, nil
|
||||
}
|
||||
|
||||
urlErr, ok := err.(*url.Error)
|
||||
if !ok {
|
||||
return false, err
|
||||
}
|
||||
httpErr, ok := urlErr.Err.(*HttpStatusError)
|
||||
if !ok {
|
||||
return false, err
|
||||
}
|
||||
if httpErr.Response.StatusCode == http.StatusNotFound {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
func (registry *Registry) BlobMetadata(repository string, digest digest.Digest) (distribution.Descriptor, error) {
|
||||
checkUrl := registry.url("/v2/%s/blobs/%s", repository, digest)
|
||||
registry.Logf("registry.blob.check url=%s repository=%s digest=%s", checkUrl, repository, digest)
|
||||
|
||||
resp, err := registry.Client.Head(checkUrl)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return distribution.Descriptor{}, err
|
||||
}
|
||||
|
||||
return distribution.Descriptor{
|
||||
Digest: digest,
|
||||
Size: resp.ContentLength,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (registry *Registry) initiateUpload(repository string) (*url.URL, error) {
|
||||
initiateUrl := registry.url("/v2/%s/blobs/uploads/", repository)
|
||||
registry.Logf("registry.blob.initiate-upload url=%s repository=%s", initiateUrl, repository)
|
||||
|
||||
resp, err := registry.Client.Post(initiateUrl, "application/octet-stream", nil)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
location := resp.Header.Get("Location")
|
||||
locationUrl, err := url.Parse(location)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return locationUrl, nil
|
||||
}
|
44
vendor/github.com/heroku/docker-registry-client/registry/errortransport.go
generated
vendored
Normal file
44
vendor/github.com/heroku/docker-registry-client/registry/errortransport.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type HttpStatusError struct {
|
||||
Response *http.Response
|
||||
Body []byte // Copied from `Response.Body` to avoid problems with unclosed bodies later. Nobody calls `err.Response.Body.Close()`, ever.
|
||||
}
|
||||
|
||||
func (err *HttpStatusError) Error() string {
|
||||
return fmt.Sprintf("http: non-successful response (status=%v body=%q)", err.Response.StatusCode, err.Body)
|
||||
}
|
||||
|
||||
var _ error = &HttpStatusError{}
|
||||
|
||||
type ErrorTransport struct {
|
||||
Transport http.RoundTripper
|
||||
}
|
||||
|
||||
func (t *ErrorTransport) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||
resp, err := t.Transport.RoundTrip(request)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("http: failed to read response body (status=%v, err=%q)", resp.StatusCode, err)
|
||||
}
|
||||
|
||||
return nil, &HttpStatusError{
|
||||
Response: resp,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
66
vendor/github.com/heroku/docker-registry-client/registry/json.go
generated
vendored
Normal file
66
vendor/github.com/heroku/docker-registry-client/registry/json.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoMorePages = errors.New("No more pages")
|
||||
)
|
||||
|
||||
func (registry *Registry) getJson(url string, response interface{}) error {
|
||||
resp, err := registry.Client.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
err = decoder.Decode(response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getPaginatedJson accepts a string and a pointer, and returns the
|
||||
// next page URL while updating pointed-to variable with a parsed JSON
|
||||
// value. When there are no more pages it returns `ErrNoMorePages`.
|
||||
func (registry *Registry) getPaginatedJson(url string, response interface{}) (string, error) {
|
||||
resp, err := registry.Client.Get(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
err = decoder.Decode(response)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return getNextLink(resp)
|
||||
}
|
||||
|
||||
// Matches an RFC 5988 (https://tools.ietf.org/html/rfc5988#section-5)
|
||||
// Link header. For example,
|
||||
//
|
||||
// <http://registry.example.com/v2/_catalog?n=5&last=tag5>; type="application/json"; rel="next"
|
||||
//
|
||||
// The URL is _supposed_ to be wrapped by angle brackets `< ... >`,
|
||||
// but e.g., quay.io does not include them. Similarly, params like
|
||||
// `rel="next"` may not have quoted values in the wild.
|
||||
var nextLinkRE = regexp.MustCompile(`^ *<?([^;>]+)>? *(?:;[^;]*)*; *rel="?next"?(?:;.*)?`)
|
||||
|
||||
func getNextLink(resp *http.Response) (string, error) {
|
||||
for _, link := range resp.Header[http.CanonicalHeaderKey("Link")] {
|
||||
parts := nextLinkRE.FindStringSubmatch(link)
|
||||
if parts != nil {
|
||||
return parts[1], nil
|
||||
}
|
||||
}
|
||||
return "", ErrNoMorePages
|
||||
}
|
126
vendor/github.com/heroku/docker-registry-client/registry/manifest.go
generated
vendored
Normal file
126
vendor/github.com/heroku/docker-registry-client/registry/manifest.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
func (registry *Registry) Manifest(repository, reference string) (*schema1.SignedManifest, error) {
|
||||
url := registry.url("/v2/%s/manifests/%s", repository, reference)
|
||||
registry.Logf("registry.manifest.get url=%s repository=%s reference=%s", url, repository, reference)
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", schema1.MediaTypeManifest)
|
||||
resp, err := registry.Client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signedManifest := &schema1.SignedManifest{}
|
||||
err = signedManifest.UnmarshalJSON(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return signedManifest, nil
|
||||
}
|
||||
|
||||
func (registry *Registry) ManifestV2(repository, reference string) (*schema2.DeserializedManifest, error) {
|
||||
url := registry.url("/v2/%s/manifests/%s", repository, reference)
|
||||
registry.Logf("registry.manifest.get url=%s repository=%s reference=%s", url, repository, reference)
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", schema2.MediaTypeManifest)
|
||||
resp, err := registry.Client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deserialized := &schema2.DeserializedManifest{}
|
||||
err = deserialized.UnmarshalJSON(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return deserialized, nil
|
||||
}
|
||||
|
||||
func (registry *Registry) ManifestDigest(repository, reference string) (digest.Digest, error) {
|
||||
url := registry.url("/v2/%s/manifests/%s", repository, reference)
|
||||
registry.Logf("registry.manifest.head url=%s repository=%s reference=%s", url, repository, reference)
|
||||
|
||||
resp, err := registry.Client.Head(url)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return digest.Parse(resp.Header.Get("Docker-Content-Digest"))
|
||||
}
|
||||
|
||||
func (registry *Registry) DeleteManifest(repository string, digest digest.Digest) error {
|
||||
url := registry.url("/v2/%s/manifests/%s", repository, digest)
|
||||
registry.Logf("registry.manifest.delete url=%s repository=%s reference=%s", url, repository, digest)
|
||||
|
||||
req, err := http.NewRequest("DELETE", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := registry.Client.Do(req)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (registry *Registry) PutManifest(repository, reference string, manifest distribution.Manifest) error {
|
||||
url := registry.url("/v2/%s/manifests/%s", repository, reference)
|
||||
registry.Logf("registry.manifest.put url=%s repository=%s reference=%s", url, repository, reference)
|
||||
|
||||
mediaType, payload, err := manifest.Payload()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buffer := bytes.NewBuffer(payload)
|
||||
req, err := http.NewRequest("PUT", url, buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", mediaType)
|
||||
resp, err := registry.Client.Do(req)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
120
vendor/github.com/heroku/docker-registry-client/registry/registry.go
generated
vendored
Normal file
120
vendor/github.com/heroku/docker-registry-client/registry/registry.go
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type LogfCallback func(format string, args ...interface{})
|
||||
|
||||
/*
|
||||
* Discard log messages silently.
|
||||
*/
|
||||
func Quiet(format string, args ...interface{}) {
|
||||
/* discard logs */
|
||||
}
|
||||
|
||||
/*
|
||||
* Pass log messages along to Go's "log" module.
|
||||
*/
|
||||
func Log(format string, args ...interface{}) {
|
||||
log.Printf(format, args...)
|
||||
}
|
||||
|
||||
type Registry struct {
|
||||
URL string
|
||||
Client *http.Client
|
||||
Logf LogfCallback
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new Registry with the given URL and credentials, then Ping()s it
|
||||
* before returning it to verify that the registry is available.
|
||||
*
|
||||
* You can, alternately, construct a Registry manually by populating the fields.
|
||||
* This passes http.DefaultTransport to WrapTransport when creating the
|
||||
* http.Client.
|
||||
*/
|
||||
func New(registryUrl, username, password string) (*Registry, error) {
|
||||
transport := http.DefaultTransport
|
||||
|
||||
return newFromTransport(registryUrl, username, password, transport, Log)
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new Registry, as with New, using an http.Transport that disables
|
||||
* SSL certificate verification.
|
||||
*/
|
||||
func NewInsecure(registryUrl, username, password string) (*Registry, error) {
|
||||
transport := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
|
||||
return newFromTransport(registryUrl, username, password, transport, Log)
|
||||
}
|
||||
|
||||
/*
|
||||
* Given an existing http.RoundTripper such as http.DefaultTransport, build the
|
||||
* transport stack necessary to authenticate to the Docker registry API. This
|
||||
* adds in support for OAuth bearer tokens and HTTP Basic auth, and sets up
|
||||
* error handling this library relies on.
|
||||
*/
|
||||
func WrapTransport(transport http.RoundTripper, url, username, password string) http.RoundTripper {
|
||||
tokenTransport := &TokenTransport{
|
||||
Transport: transport,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
basicAuthTransport := &BasicTransport{
|
||||
Transport: tokenTransport,
|
||||
URL: url,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
errorTransport := &ErrorTransport{
|
||||
Transport: basicAuthTransport,
|
||||
}
|
||||
return errorTransport
|
||||
}
|
||||
|
||||
func newFromTransport(registryUrl, username, password string, transport http.RoundTripper, logf LogfCallback) (*Registry, error) {
|
||||
url := strings.TrimSuffix(registryUrl, "/")
|
||||
transport = WrapTransport(transport, url, username, password)
|
||||
registry := &Registry{
|
||||
URL: url,
|
||||
Client: &http.Client{
|
||||
Transport: transport,
|
||||
},
|
||||
Logf: logf,
|
||||
}
|
||||
|
||||
if err := registry.Ping(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return registry, nil
|
||||
}
|
||||
|
||||
func (r *Registry) url(pathTemplate string, args ...interface{}) string {
|
||||
pathSuffix := fmt.Sprintf(pathTemplate, args...)
|
||||
url := fmt.Sprintf("%s%s", r.URL, pathSuffix)
|
||||
return url
|
||||
}
|
||||
|
||||
func (r *Registry) Ping() error {
|
||||
url := r.url("/v2/")
|
||||
r.Logf("registry.ping url=%s", url)
|
||||
resp, err := r.Client.Get(url)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
return err
|
||||
}
|
26
vendor/github.com/heroku/docker-registry-client/registry/repositories.go
generated
vendored
Normal file
26
vendor/github.com/heroku/docker-registry-client/registry/repositories.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package registry
|
||||
|
||||
type repositoriesResponse struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
|
||||
func (registry *Registry) Repositories() ([]string, error) {
|
||||
url := registry.url("/v2/_catalog")
|
||||
repos := make([]string, 0, 10)
|
||||
var err error //We create this here, otherwise url will be rescoped with :=
|
||||
var response repositoriesResponse
|
||||
for {
|
||||
registry.Logf("registry.repositories url=%s", url)
|
||||
url, err = registry.getPaginatedJson(url, &response)
|
||||
switch err {
|
||||
case ErrNoMorePages:
|
||||
repos = append(repos, response.Repositories...)
|
||||
return repos, nil
|
||||
case nil:
|
||||
repos = append(repos, response.Repositories...)
|
||||
continue
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
25
vendor/github.com/heroku/docker-registry-client/registry/tags.go
generated
vendored
Normal file
25
vendor/github.com/heroku/docker-registry-client/registry/tags.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package registry
|
||||
|
||||
type tagsResponse struct {
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
func (registry *Registry) Tags(repository string) (tags []string, err error) {
|
||||
url := registry.url("/v2/%s/tags/list", repository)
|
||||
|
||||
var response tagsResponse
|
||||
for {
|
||||
registry.Logf("registry.tags url=%s repository=%s", url, repository)
|
||||
url, err = registry.getPaginatedJson(url, &response)
|
||||
switch err {
|
||||
case ErrNoMorePages:
|
||||
tags = append(tags, response.Tags...)
|
||||
return tags, nil
|
||||
case nil:
|
||||
tags = append(tags, response.Tags...)
|
||||
continue
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
127
vendor/github.com/heroku/docker-registry-client/registry/tokentransport.go
generated
vendored
Normal file
127
vendor/github.com/heroku/docker-registry-client/registry/tokentransport.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type TokenTransport struct {
|
||||
Transport http.RoundTripper
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func (t *TokenTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
resp, err := t.Transport.RoundTrip(req)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
if authService := isTokenDemand(resp); authService != nil {
|
||||
resp, err = t.authAndRetry(authService, req)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
type authToken struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
func (t *TokenTransport) authAndRetry(authService *authService, req *http.Request) (*http.Response, error) {
|
||||
token, authResp, err := t.auth(authService)
|
||||
if err != nil {
|
||||
return authResp, err
|
||||
}
|
||||
|
||||
retryResp, err := t.retry(req, token)
|
||||
return retryResp, err
|
||||
}
|
||||
|
||||
func (t *TokenTransport) auth(authService *authService) (string, *http.Response, error) {
|
||||
authReq, err := authService.Request(t.Username, t.Password)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
client := http.Client{
|
||||
Transport: t.Transport,
|
||||
}
|
||||
|
||||
response, err := client.Do(authReq)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return "", response, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
var authToken authToken
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
err = decoder.Decode(&authToken)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return authToken.Token, nil, nil
|
||||
}
|
||||
|
||||
func (t *TokenTransport) retry(req *http.Request, token string) (*http.Response, error) {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
resp, err := t.Transport.RoundTrip(req)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
type authService struct {
|
||||
Realm string
|
||||
Service string
|
||||
Scope string
|
||||
}
|
||||
|
||||
func (authService *authService) Request(username, password string) (*http.Request, error) {
|
||||
url, err := url.Parse(authService.Realm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q := url.Query()
|
||||
q.Set("service", authService.Service)
|
||||
if authService.Scope != "" {
|
||||
q.Set("scope", authService.Scope)
|
||||
}
|
||||
url.RawQuery = q.Encode()
|
||||
|
||||
request, err := http.NewRequest("GET", url.String(), nil)
|
||||
|
||||
if username != "" || password != "" {
|
||||
request.SetBasicAuth(username, password)
|
||||
}
|
||||
|
||||
return request, err
|
||||
}
|
||||
|
||||
func isTokenDemand(resp *http.Response) *authService {
|
||||
if resp == nil {
|
||||
return nil
|
||||
}
|
||||
if resp.StatusCode != http.StatusUnauthorized {
|
||||
return nil
|
||||
}
|
||||
return parseOauthHeader(resp)
|
||||
}
|
||||
|
||||
func parseOauthHeader(resp *http.Response) *authService {
|
||||
challenges := parseAuthHeader(resp.Header)
|
||||
for _, challenge := range challenges {
|
||||
if challenge.Scheme == "bearer" {
|
||||
return &authService{
|
||||
Realm: challenge.Parameters["realm"],
|
||||
Service: challenge.Parameters["service"],
|
||||
Scope: challenge.Parameters["scope"],
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user