add better generate
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
This commit is contained in:
parent
3fc6abf56b
commit
cdd93563f5
5655 changed files with 1187011 additions and 392 deletions
69
vendor/github.com/genuinetools/reg/registry/authchallenge.go
generated
vendored
Normal file
69
vendor/github.com/genuinetools/reg/registry/authchallenge.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
bearerRegex = regexp.MustCompile(
|
||||
`^\s*Bearer\s+(.*)$`)
|
||||
basicRegex = regexp.MustCompile(`^\s*Basic\s+.*$`)
|
||||
)
|
||||
|
||||
func parseAuthHeader(header http.Header) (*authService, error) {
|
||||
ch, err := parseChallenge(header.Get("www-authenticate"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func parseChallenge(challengeHeader string) (*authService, error) {
|
||||
if basicRegex.MatchString(challengeHeader) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
match := bearerRegex.FindAllStringSubmatch(challengeHeader, -1)
|
||||
if d := len(match); d != 1 {
|
||||
return nil, fmt.Errorf("malformed auth challenge header: '%s', %d", challengeHeader, d)
|
||||
}
|
||||
parts := strings.Split(strings.TrimSpace(match[0][1]), ",")
|
||||
|
||||
var realm, service string
|
||||
var scope []string
|
||||
for _, s := range parts {
|
||||
p := strings.SplitN(s, "=", 2)
|
||||
if len(p) != 2 {
|
||||
return nil, fmt.Errorf("malformed auth challenge header: '%s'", challengeHeader)
|
||||
}
|
||||
key := p[0]
|
||||
value := strings.TrimSuffix(strings.TrimPrefix(p[1], `"`), `"`)
|
||||
switch key {
|
||||
case "realm":
|
||||
realm = value
|
||||
case "service":
|
||||
service = value
|
||||
case "scope":
|
||||
scope = strings.Fields(value)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown field in challege header %s: %v", key, challengeHeader)
|
||||
}
|
||||
}
|
||||
parsedRealm, err := url.Parse(realm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a := &authService{
|
||||
Realm: parsedRealm,
|
||||
Service: service,
|
||||
Scope: scope,
|
||||
}
|
||||
|
||||
return a, nil
|
||||
}
|
55
vendor/github.com/genuinetools/reg/registry/authchallenge_test.go
generated
vendored
Normal file
55
vendor/github.com/genuinetools/reg/registry/authchallenge_test.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type authServiceMock struct {
|
||||
service string
|
||||
realm string
|
||||
scope []string
|
||||
}
|
||||
|
||||
type challengeTestCase struct {
|
||||
header string
|
||||
errorString string
|
||||
value authServiceMock
|
||||
}
|
||||
|
||||
func (asm authServiceMock) equalTo(v *authService) bool {
|
||||
if asm.service != v.Service {
|
||||
return false
|
||||
}
|
||||
if reflect.DeepEqual(asm.scope, v.Scope) {
|
||||
return false
|
||||
}
|
||||
if asm.realm != v.Realm.String() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestParseChallenge(t *testing.T) {
|
||||
challengeHeaderCases := []challengeTestCase{
|
||||
{
|
||||
header: `Bearer realm="https://foobar.com/api/v1/token",service=foobar.com,scope=""`,
|
||||
value: authServiceMock{
|
||||
service: "foobar.com",
|
||||
realm: "https://foobar.com/api/v1/token",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range challengeHeaderCases {
|
||||
val, err := parseChallenge(tc.header)
|
||||
if err != nil && !strings.Contains(err.Error(), tc.errorString) {
|
||||
t.Fatalf("expected error to contain %v, got %s", tc.errorString, err)
|
||||
}
|
||||
if !tc.value.equalTo(val) {
|
||||
t.Fatalf("got %v, expected %v", val, tc.value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
25
vendor/github.com/genuinetools/reg/registry/basictransport.go
generated
vendored
Normal file
25
vendor/github.com/genuinetools/reg/registry/basictransport.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BasicTransport defines the data structure for authentication via basic auth.
|
||||
type BasicTransport struct {
|
||||
Transport http.RoundTripper
|
||||
URL string
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// RoundTrip defines the round tripper for basic auth transport.
|
||||
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
|
||||
}
|
39
vendor/github.com/genuinetools/reg/registry/catalog.go
generated
vendored
Normal file
39
vendor/github.com/genuinetools/reg/registry/catalog.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/peterhellberg/link"
|
||||
)
|
||||
|
||||
type catalogResponse struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
|
||||
// Catalog returns the repositories in a registry.
|
||||
func (r *Registry) Catalog(u string) ([]string, error) {
|
||||
if u == "" {
|
||||
u = "/v2/_catalog"
|
||||
}
|
||||
uri := r.url(u)
|
||||
r.Logf("registry.catalog url=%s", uri)
|
||||
|
||||
var response catalogResponse
|
||||
h, err := r.getJSON(uri, &response, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, l := range link.ParseHeader(h) {
|
||||
if l.Rel == "next" {
|
||||
unescaped, _ := url.QueryUnescape(l.URI)
|
||||
repos, err := r.Catalog(unescaped)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.Repositories = append(response.Repositories, repos...)
|
||||
}
|
||||
}
|
||||
|
||||
return response.Repositories, nil
|
||||
}
|
47
vendor/github.com/genuinetools/reg/registry/delete.go
generated
vendored
Normal file
47
vendor/github.com/genuinetools/reg/registry/delete.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
ocd "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// Delete removes a repository digest or reference from the registry.
|
||||
// https://docs.docker.com/registry/spec/api/#deleting-an-image
|
||||
func (r *Registry) Delete(repository, digest string) error {
|
||||
// If digest is not valid try resolving it as a reference
|
||||
if _, err := ocd.Parse(digest); err != nil {
|
||||
digest, err = r.Digest(repository, digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if digest == "" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the image.
|
||||
url := r.url("/v2/%s/manifests/%s", repository, digest)
|
||||
r.Logf("registry.manifests.delete url=%s repository=%s digest=%s",
|
||||
url, repository, digest)
|
||||
|
||||
req, err := http.NewRequest("DELETE", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", schema2.MediaTypeManifest)
|
||||
resp, err := r.Client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusAccepted || resp.StatusCode == http.StatusNotFound {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Got status code: %d", resp.StatusCode)
|
||||
}
|
34
vendor/github.com/genuinetools/reg/registry/digest.go
generated
vendored
Normal file
34
vendor/github.com/genuinetools/reg/registry/digest.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
)
|
||||
|
||||
// Digest returns the digest for a repository and reference.
|
||||
func (r *Registry) Digest(repository, ref string) (string, error) {
|
||||
url := r.url("/v2/%s/manifests/%s", repository, ref)
|
||||
r.Logf("registry.manifests.get url=%s repository=%s ref=%s",
|
||||
url, repository, ref)
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("Accept", schema2.MediaTypeManifest)
|
||||
|
||||
resp, err := r.Client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound {
|
||||
return "", fmt.Errorf("Got status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
digest := resp.Header.Get("Docker-Content-Digest")
|
||||
return digest, nil
|
||||
}
|
46
vendor/github.com/genuinetools/reg/registry/errortransport.go
generated
vendored
Normal file
46
vendor/github.com/genuinetools/reg/registry/errortransport.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
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{}
|
||||
|
||||
// ErrorTransport defines the data structure for returning errors from the round tripper.
|
||||
type ErrorTransport struct {
|
||||
Transport http.RoundTripper
|
||||
}
|
||||
|
||||
// RoundTrip defines the round tripper for the error transport.
|
||||
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 >= 500 || resp.StatusCode == http.StatusUnauthorized {
|
||||
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
|
||||
}
|
89
vendor/github.com/genuinetools/reg/registry/layer.go
generated
vendored
Normal file
89
vendor/github.com/genuinetools/reg/registry/layer.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// DownloadLayer downloads a specific layer by digest for a repository.
|
||||
func (r *Registry) DownloadLayer(repository string, digest digest.Digest) (io.ReadCloser, error) {
|
||||
url := r.url("/v2/%s/blobs/%s", repository, digest)
|
||||
r.Logf("registry.layer.download url=%s repository=%s digest=%s", url, repository, digest)
|
||||
|
||||
resp, err := r.Client.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Body, nil
|
||||
}
|
||||
|
||||
// UploadLayer uploads a specific layer by digest for a repository.
|
||||
func (r *Registry) UploadLayer(repository string, digest reference.Reference, content io.Reader) error {
|
||||
uploadURL, err := r.initiateUpload(repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
q := uploadURL.Query()
|
||||
q.Set("digest", digest.String())
|
||||
uploadURL.RawQuery = q.Encode()
|
||||
|
||||
r.Logf("registry.layer.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 = r.Client.Do(upload)
|
||||
return err
|
||||
}
|
||||
|
||||
// HasLayer returns if the registry contains the specific digest for a repository.
|
||||
func (r *Registry) HasLayer(repository string, digest digest.Digest) (bool, error) {
|
||||
checkURL := r.url("/v2/%s/blobs/%s", repository, digest)
|
||||
r.Logf("registry.layer.check url=%s repository=%s digest=%s", checkURL, repository, digest)
|
||||
|
||||
resp, err := r.Client.Head(checkURL)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
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 (r *Registry) initiateUpload(repository string) (*url.URL, error) {
|
||||
initiateURL := r.url("/v2/%s/blobs/uploads/", repository)
|
||||
r.Logf("registry.layer.initiate-upload url=%s repository=%s", initiateURL, repository)
|
||||
|
||||
resp, err := r.Client.Post(initiateURL, "application/octet-stream", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
location := resp.Header.Get("Location")
|
||||
locationURL, err := url.Parse(location)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return locationURL, nil
|
||||
}
|
86
vendor/github.com/genuinetools/reg/registry/manifest.go
generated
vendored
Normal file
86
vendor/github.com/genuinetools/reg/registry/manifest.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/manifest/manifestlist"
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
)
|
||||
|
||||
// Manifest returns the manifest for a specific repository:tag.
|
||||
func (r *Registry) Manifest(repository, ref string) (distribution.Manifest, error) {
|
||||
uri := r.url("/v2/%s/manifests/%s", repository, ref)
|
||||
r.Logf("registry.manifests uri=%s repository=%s ref=%s", uri, repository, ref)
|
||||
|
||||
req, err := http.NewRequest("GET", uri, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Accept", schema2.MediaTypeManifest)
|
||||
req.Header.Add("Accept", manifestlist.MediaTypeManifestList)
|
||||
|
||||
resp, err := r.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
|
||||
}
|
||||
r.Logf("registry.manifests resp.Status=%s, body=%s", resp.Status, body)
|
||||
|
||||
m, _, err := distribution.UnmarshalManifest(resp.Header.Get("Content-Type"), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// ManifestList gets the registry v2 manifest list.
|
||||
func (r *Registry) ManifestList(repository, ref string) (manifestlist.ManifestList, error) {
|
||||
uri := r.url("/v2/%s/manifests/%s", repository, ref)
|
||||
r.Logf("registry.manifests uri=%s repository=%s ref=%s", uri, repository, ref)
|
||||
|
||||
var m manifestlist.ManifestList
|
||||
if _, err := r.getJSON(uri, &m, true); err != nil {
|
||||
r.Logf("registry.manifests response=%v", m)
|
||||
return m, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// ManifestV2 gets the registry v2 manifest.
|
||||
func (r *Registry) ManifestV2(repository, ref string) (schema2.Manifest, error) {
|
||||
uri := r.url("/v2/%s/manifests/%s", repository, ref)
|
||||
r.Logf("registry.manifests uri=%s repository=%s ref=%s", uri, repository, ref)
|
||||
|
||||
var m schema2.Manifest
|
||||
if _, err := r.getJSON(uri, &m, true); err != nil {
|
||||
r.Logf("registry.manifests response=%v", m)
|
||||
return m, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// ManifestV1 gets the registry v1 manifest.
|
||||
func (r *Registry) ManifestV1(repository, ref string) (schema1.SignedManifest, error) {
|
||||
uri := r.url("/v2/%s/manifests/%s", repository, ref)
|
||||
r.Logf("registry.manifests uri=%s repository=%s ref=%s", uri, repository, ref)
|
||||
|
||||
var m schema1.SignedManifest
|
||||
if _, err := r.getJSON(uri, &m, false); err != nil {
|
||||
r.Logf("registry.manifests response=%v", m)
|
||||
return m, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
12
vendor/github.com/genuinetools/reg/registry/ping.go
generated
vendored
Normal file
12
vendor/github.com/genuinetools/reg/registry/ping.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
package registry
|
||||
|
||||
// Ping tries to contact a registry URL to make sure it is up and accessible.
|
||||
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()
|
||||
}
|
||||
return err
|
||||
}
|
133
vendor/github.com/genuinetools/reg/registry/registry.go
generated
vendored
Normal file
133
vendor/github.com/genuinetools/reg/registry/registry.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/manifest/manifestlist"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
// Registry defines the client for retriving information from the registry API.
|
||||
type Registry struct {
|
||||
URL string
|
||||
Domain string
|
||||
Username string
|
||||
Password string
|
||||
Client *http.Client
|
||||
Logf LogfCallback
|
||||
}
|
||||
|
||||
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...)
|
||||
}
|
||||
|
||||
// New creates a new Registry struct with the given URL and credentials.
|
||||
func New(auth types.AuthConfig, debug bool) (*Registry, error) {
|
||||
transport := http.DefaultTransport
|
||||
|
||||
return newFromTransport(auth, transport, debug)
|
||||
}
|
||||
|
||||
// NewInsecure creates a new Registry struct with the given URL and credentials,
|
||||
// using a http.Transport that will not verify an SSL certificate.
|
||||
func NewInsecure(auth types.AuthConfig, debug bool) (*Registry, error) {
|
||||
transport := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
|
||||
return newFromTransport(auth, transport, debug)
|
||||
}
|
||||
|
||||
func newFromTransport(auth types.AuthConfig, transport http.RoundTripper, debug bool) (*Registry, error) {
|
||||
url := strings.TrimSuffix(auth.ServerAddress, "/")
|
||||
|
||||
if !reProtocol.MatchString(url) {
|
||||
url = "https://" + url
|
||||
}
|
||||
|
||||
tokenTransport := &TokenTransport{
|
||||
Transport: transport,
|
||||
Username: auth.Username,
|
||||
Password: auth.Password,
|
||||
}
|
||||
basicAuthTransport := &BasicTransport{
|
||||
Transport: tokenTransport,
|
||||
URL: url,
|
||||
Username: auth.Username,
|
||||
Password: auth.Password,
|
||||
}
|
||||
errorTransport := &ErrorTransport{
|
||||
Transport: basicAuthTransport,
|
||||
}
|
||||
|
||||
// set the logging
|
||||
logf := Quiet
|
||||
if debug {
|
||||
logf = Log
|
||||
}
|
||||
|
||||
registry := &Registry{
|
||||
URL: url,
|
||||
Domain: reProtocol.ReplaceAllString(url, ""),
|
||||
Client: &http.Client{
|
||||
Transport: errorTransport,
|
||||
},
|
||||
Username: auth.Username,
|
||||
Password: auth.Password,
|
||||
Logf: logf,
|
||||
}
|
||||
|
||||
if err := registry.Ping(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
req.Header.Add("Accept", schema2.MediaTypeManifest)
|
||||
req.Header.Add("Accept", manifestlist.MediaTypeManifestList)
|
||||
}
|
||||
resp, err := r.Client.Do(req)
|
||||
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
|
||||
}
|
18
vendor/github.com/genuinetools/reg/registry/tags.go
generated
vendored
Normal file
18
vendor/github.com/genuinetools/reg/registry/tags.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
package registry
|
||||
|
||||
type tagsResponse struct {
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
// Tags returns the tags for a specific repository.
|
||||
func (r *Registry) Tags(repository string) ([]string, error) {
|
||||
url := r.url("/v2/%s/tags/list", repository)
|
||||
r.Logf("registry.tags url=%s repository=%s", url, repository)
|
||||
|
||||
var response tagsResponse
|
||||
if _, err := r.getJSON(url, &response, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.Tags, nil
|
||||
}
|
166
vendor/github.com/genuinetools/reg/registry/tokentransport.go
generated
vendored
Normal file
166
vendor/github.com/genuinetools/reg/registry/tokentransport.go
generated
vendored
Normal file
|
@ -0,0 +1,166 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// TokenTransport defines the data structure for authentication via tokens.
|
||||
type TokenTransport struct {
|
||||
Transport http.RoundTripper
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// RoundTrip defines the round tripper for token transport.
|
||||
func (t *TokenTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
resp, err := t.Transport.RoundTrip(req)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
authService, err := isTokenDemand(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if authService == nil {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
return t.authAndRetry(authService, req)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return t.retry(req, token)
|
||||
}
|
||||
|
||||
func (t *TokenTransport) auth(authService *authService) (string, *http.Response, error) {
|
||||
authReq, err := authService.Request(t.Username, t.Password)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
c := http.Client{
|
||||
Transport: t.Transport,
|
||||
}
|
||||
|
||||
resp, err := c.Do(authReq)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", resp, err
|
||||
}
|
||||
|
||||
var authToken authToken
|
||||
if err := json.NewDecoder(resp.Body).Decode(&authToken); 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))
|
||||
return t.Transport.RoundTrip(req)
|
||||
}
|
||||
|
||||
type authService struct {
|
||||
Realm *url.URL
|
||||
Service string
|
||||
Scope []string
|
||||
}
|
||||
|
||||
func (a *authService) Request(username, password string) (*http.Request, error) {
|
||||
q := a.Realm.Query()
|
||||
q.Set("service", a.Service)
|
||||
for _, s := range a.Scope {
|
||||
q.Set("scope", s)
|
||||
}
|
||||
// q.Set("scope", "repository:r.j3ss.co/htop:push,pull")
|
||||
a.Realm.RawQuery = q.Encode()
|
||||
|
||||
req, err := http.NewRequest("GET", a.Realm.String(), nil)
|
||||
|
||||
if username != "" || password != "" {
|
||||
req.SetBasicAuth(username, password)
|
||||
}
|
||||
|
||||
return req, err
|
||||
}
|
||||
|
||||
func isTokenDemand(resp *http.Response) (*authService, error) {
|
||||
if resp == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if resp.StatusCode != http.StatusUnauthorized {
|
||||
return nil, nil
|
||||
}
|
||||
return parseAuthHeader(resp.Header)
|
||||
}
|
||||
|
||||
// Token returns the required token for the specific resource url.
|
||||
func (r *Registry) Token(url string) (string, error) {
|
||||
r.Logf("registry.token url=%s", url)
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
resp, err := r.Client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
a, err := isTokenDemand(resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if a == nil {
|
||||
r.Logf("registry.token authService=nil")
|
||||
return "", nil
|
||||
}
|
||||
|
||||
authReq, err := a.Request(r.Username, r.Password)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp, err = r.Client.Do(authReq)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("Getting token failed with StatusCode != StatusOK but %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var authToken authToken
|
||||
if err := json.NewDecoder(resp.Body).Decode(&authToken); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if authToken.Token == "" {
|
||||
return "", errors.New("Auth token cannot be empty")
|
||||
}
|
||||
|
||||
return authToken.Token, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue