Moved imported orca docs into ucp directory
This commit is contained in:
parent
a56d36fdaa
commit
b2da4f338c
7 changed files with 0 additions and 558 deletions
|
@ -1,42 +0,0 @@
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/docker/orca"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
MockRegistry struct {
|
|
||||||
orca.RegistryConfig
|
|
||||||
client *orca.RegistryClient
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewRegistry(reg *orca.RegistryConfig) (orca.Registry, error) {
|
|
||||||
u, err := url.Parse(reg.URL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rClient := &orca.RegistryClient{
|
|
||||||
URL: u,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &MockRegistry{
|
|
||||||
RegistryConfig: *reg,
|
|
||||||
client: rClient,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *MockRegistry) GetAuthToken(username, accessType, hostname, reponame string) (string, error) {
|
|
||||||
return "foo", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *MockRegistry) GetConfig() *orca.RegistryConfig {
|
|
||||||
return &r.RegistryConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *MockRegistry) GetTransport() http.RoundTripper {
|
|
||||||
return r.client.HttpClient.Transport
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
# Docker Registry Go lib
|
|
||||||
This is a simple Go package to use with the Docker Registry v1.
|
|
||||||
|
|
||||||
# Example
|
|
||||||
|
|
||||||
```
|
|
||||||
import registry "github.com/ehazlett/orca/registry/v1"
|
|
||||||
|
|
||||||
// make sure to handle the err
|
|
||||||
client, _ := registry.NewRegistryClient("http://localhost:5000", nil)
|
|
||||||
|
|
||||||
res, _ := client.Search("busybox", 1, 100)
|
|
||||||
|
|
||||||
fmt.Printf("Number of Repositories: %d\n", res.NumberOfResults)
|
|
||||||
for _, r := range res.Results {
|
|
||||||
fmt.Printf(" - Name: %s\n", r.Name)
|
|
||||||
}
|
|
||||||
```
|
|
|
@ -1,15 +0,0 @@
|
||||||
package v1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Error struct {
|
|
||||||
StatusCode int
|
|
||||||
Status string
|
|
||||||
msg string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e Error) Error() string {
|
|
||||||
return fmt.Sprintf("%s: %s", e.Status, e.msg)
|
|
||||||
}
|
|
|
@ -1,277 +0,0 @@
|
||||||
package v1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrNotFound = errors.New("Not found")
|
|
||||||
defaultHTTPTimeout = 30 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
type RegistryClient struct {
|
|
||||||
URL *url.URL
|
|
||||||
tlsConfig *tls.Config
|
|
||||||
httpClient *http.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
type Repo struct {
|
|
||||||
Namespace string
|
|
||||||
Repository string
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseRepo(repo string) Repo {
|
|
||||||
namespace := "library"
|
|
||||||
r := repo
|
|
||||||
|
|
||||||
if strings.Index(repo, "/") != -1 {
|
|
||||||
parts := strings.Split(repo, "/")
|
|
||||||
namespace = parts[0]
|
|
||||||
r = path.Join(parts[1:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Repo{
|
|
||||||
Namespace: namespace,
|
|
||||||
Repository: r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHTTPClient(u *url.URL, tlsConfig *tls.Config, timeout time.Duration) *http.Client {
|
|
||||||
httpTransport := &http.Transport{
|
|
||||||
TLSClientConfig: tlsConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
httpTransport.Dial = func(proto, addr string) (net.Conn, error) {
|
|
||||||
return net.DialTimeout(proto, addr, timeout)
|
|
||||||
}
|
|
||||||
return &http.Client{Transport: httpTransport}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRegistryClient(registryUrl string, tlsConfig *tls.Config) (*RegistryClient, error) {
|
|
||||||
u, err := url.Parse(registryUrl)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
httpClient := newHTTPClient(u, tlsConfig, defaultHTTPTimeout)
|
|
||||||
return &RegistryClient{
|
|
||||||
URL: u,
|
|
||||||
httpClient: httpClient,
|
|
||||||
tlsConfig: tlsConfig,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *RegistryClient) doRequest(method string, path string, body []byte, headers map[string]string) ([]byte, error) {
|
|
||||||
b := bytes.NewBuffer(body)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(method, client.URL.String()+"/v1"+path, b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
if headers != nil {
|
|
||||||
for header, value := range headers {
|
|
||||||
req.Header.Add(header, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := client.httpClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
if !strings.Contains(err.Error(), "connection refused") && client.tlsConfig == nil {
|
|
||||||
return nil, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled endpoint without TLS?", err)
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode == 404 {
|
|
||||||
return nil, ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode >= 400 {
|
|
||||||
return nil, Error{StatusCode: resp.StatusCode, Status: resp.Status, msg: string(data)}
|
|
||||||
}
|
|
||||||
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *RegistryClient) Search(query string, page int, numResults int) (*SearchResult, error) {
|
|
||||||
if numResults < 1 {
|
|
||||||
numResults = 100
|
|
||||||
}
|
|
||||||
uri := fmt.Sprintf("/search?q=%s&n=%d&page=%d", query, numResults, page)
|
|
||||||
data, err := client.doRequest("GET", uri, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res := &SearchResult{}
|
|
||||||
if err := json.Unmarshal(data, &res); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *RegistryClient) DeleteRepository(repo string) error {
|
|
||||||
r := parseRepo(repo)
|
|
||||||
uri := fmt.Sprintf("/repositories/%s/%s/", r.Namespace, r.Repository)
|
|
||||||
if _, err := client.doRequest("DELETE", uri, nil, nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *RegistryClient) DeleteTag(repo string, tag string) error {
|
|
||||||
r := parseRepo(repo)
|
|
||||||
uri := fmt.Sprintf("/repositories/%s/%s/tags/%s", r.Namespace, r.Repository, tag)
|
|
||||||
if _, err := client.doRequest("DELETE", uri, nil, nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *RegistryClient) Layer(id string) (*Layer, error) {
|
|
||||||
uri := fmt.Sprintf("/images/%s/json", id)
|
|
||||||
data, err := client.doRequest("GET", uri, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
layer := &Layer{}
|
|
||||||
if err := json.Unmarshal(data, &layer); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return layer, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *RegistryClient) loadLayer(name, id string) ([]Layer, []Tag, int64, error) {
|
|
||||||
uri := fmt.Sprintf("/images/%s/json", id)
|
|
||||||
layer := Layer{}
|
|
||||||
layers := []Layer{}
|
|
||||||
tags := []Tag{}
|
|
||||||
size := int64(0)
|
|
||||||
|
|
||||||
data, err := client.doRequest("GET", uri, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.Unmarshal(data, &layer); err != nil {
|
|
||||||
return nil, nil, -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
uri = fmt.Sprintf("/images/%s/ancestry", id)
|
|
||||||
|
|
||||||
ancestry := []string{}
|
|
||||||
|
|
||||||
data, err = client.doRequest("GET", uri, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = json.Unmarshal(data, &ancestry); err != nil {
|
|
||||||
return nil, nil, -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tag := Tag{
|
|
||||||
ID: id,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
tags = append(tags, tag)
|
|
||||||
layer.Ancestry = ancestry
|
|
||||||
|
|
||||||
layers = append(layers, layer)
|
|
||||||
// parse ancestor layers
|
|
||||||
for _, i := range ancestry {
|
|
||||||
uri = fmt.Sprintf("/images/%s/json", i)
|
|
||||||
l := &Layer{}
|
|
||||||
|
|
||||||
data, err = client.doRequest("GET", uri, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = json.Unmarshal(data, &l); err != nil {
|
|
||||||
return nil, nil, -1, err
|
|
||||||
}
|
|
||||||
size += l.Size
|
|
||||||
layers = append(layers, *l)
|
|
||||||
}
|
|
||||||
|
|
||||||
return layers, tags, size, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *RegistryClient) Repository(name string) (*Repository, error) {
|
|
||||||
r := parseRepo(name)
|
|
||||||
uri := fmt.Sprintf("/repositories/%s/%s/tags", r.Namespace, r.Repository)
|
|
||||||
|
|
||||||
repository := &Repository{
|
|
||||||
Name: path.Join(r.Namespace, r.Repository),
|
|
||||||
Namespace: r.Namespace,
|
|
||||||
Repository: r.Repository,
|
|
||||||
}
|
|
||||||
|
|
||||||
// HACK: check for hub url and return
|
|
||||||
// used in orca catalog
|
|
||||||
baseURL := client.URL.String()
|
|
||||||
if strings.Contains(baseURL, "index.docker.io") {
|
|
||||||
return repository, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var repoTags map[string]string
|
|
||||||
|
|
||||||
data, err := client.doRequest("GET", uri, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.Unmarshal(data, &repoTags); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
layers := []Layer{}
|
|
||||||
tags := []Tag{}
|
|
||||||
size := int64(0)
|
|
||||||
|
|
||||||
for n, id := range repoTags {
|
|
||||||
l, t, s, err := client.loadLayer(n, id)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("error loading layer: id=%s", id)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
layers = append(layers, l...)
|
|
||||||
tags = append(tags, t...)
|
|
||||||
size += s
|
|
||||||
}
|
|
||||||
|
|
||||||
repository.Tags = tags
|
|
||||||
repository.Layers = layers
|
|
||||||
repository.Size = int64(size) / int64(len(tags))
|
|
||||||
|
|
||||||
return repository, nil
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
package v1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
Tag struct {
|
|
||||||
ID string
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
ContainerConfig struct {
|
|
||||||
types.ContainerJSON
|
|
||||||
Cmd []string `json:"Cmd,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
Layer struct {
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
Parent string `json:"parent,omitempty"`
|
|
||||||
Created *time.Time `json:"created,omitempty"`
|
|
||||||
Container string `json:"container,omitempty"`
|
|
||||||
ContainerConfig *ContainerConfig `json:"container_config,omitempty"`
|
|
||||||
DockerVersion string `json:"docker_version,omitempty"`
|
|
||||||
Author string `json:"author,omitempty"`
|
|
||||||
Architecture string `json:"architecture,omitempty"`
|
|
||||||
OS string `json:"os,omitempty"`
|
|
||||||
Size int64 `json:"size,omitempty"`
|
|
||||||
Ancestry []string `json:"ancestry,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
Repository struct {
|
|
||||||
Description string `json:"description,omitempty"`
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Namespace string `json:"namespace,omitempty"`
|
|
||||||
Repository string `json:"repository,omitempty"`
|
|
||||||
Tags []Tag `json:"tags,omitempty"`
|
|
||||||
Layers []Layer `json:"layers,omitempty"`
|
|
||||||
Size int64 `json:"size,omitempty"`
|
|
||||||
// these are only for the official index
|
|
||||||
Trusted bool `json:"is_trusted,omitempty"`
|
|
||||||
Official bool `json:"is_official,omitempty"`
|
|
||||||
StarCount int `json:"star_count,omitempty"`
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -1,10 +0,0 @@
|
||||||
package v1
|
|
||||||
|
|
||||||
type (
|
|
||||||
SearchResult struct {
|
|
||||||
NumberOfResults int `json:"num_results,omitempty"`
|
|
||||||
NumberOfPages int `json:"num_pages,omitempty"`
|
|
||||||
Query string `json:"query,omitempty"`
|
|
||||||
Results []*Repository `json:"results,omitempty"`
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -1,149 +0,0 @@
|
||||||
package v2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
"github.com/docker/orca"
|
|
||||||
"github.com/docker/orca/auth"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrNotFound = errors.New("Not found")
|
|
||||||
defaultHTTPTimeout = 30 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
AuthToken struct {
|
|
||||||
Token string `json:"token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
V2Registry struct {
|
|
||||||
orca.RegistryConfig
|
|
||||||
client *orca.RegistryClient
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewRegistry(reg *orca.RegistryConfig, swarmTLSConfig *tls.Config) (orca.Registry, error) {
|
|
||||||
// sanity check the registry settings
|
|
||||||
u, err := url.Parse(reg.URL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("The provided Docker Trusted Registry URL was malformed and could not be parsed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new TLS config for the registry, based on swarm's
|
|
||||||
// This will allow us not to mess with the Swarm RootCAs
|
|
||||||
tlsConfig := *swarmTLSConfig
|
|
||||||
tlsConfig.InsecureSkipVerify = reg.Insecure
|
|
||||||
if reg.CACert != "" {
|
|
||||||
// If the user specified a CA, create a new RootCA pool containing only that CA cert.
|
|
||||||
log.Debugf("cert: %s", reg.CACert)
|
|
||||||
certPool := x509.NewCertPool()
|
|
||||||
certPool.AppendCertsFromPEM([]byte(reg.CACert))
|
|
||||||
tlsConfig.RootCAs = certPool
|
|
||||||
log.Debug("Connecting to Registry with user-provided CA")
|
|
||||||
} else {
|
|
||||||
// If the user did not specify a CA, fall back to the system's Root CAs
|
|
||||||
tlsConfig.RootCAs = nil
|
|
||||||
log.Debug("Connecting to Registry with system Root CAs")
|
|
||||||
}
|
|
||||||
|
|
||||||
httpClient := &http.Client{
|
|
||||||
Transport: &http.Transport{TLSClientConfig: &tlsConfig},
|
|
||||||
Timeout: defaultHTTPTimeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
rClient := &orca.RegistryClient{
|
|
||||||
URL: u,
|
|
||||||
HttpClient: httpClient,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &V2Registry{
|
|
||||||
RegistryConfig: *reg,
|
|
||||||
client: rClient,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *V2Registry) doRequest(method string, path string, body []byte, headers map[string]string, username string) ([]byte, error) {
|
|
||||||
b := bytes.NewBuffer(body)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(method, path, b)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("couldn't create request: %s", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// The DTR Auth server will validate the UCP client cert and will grant access to whatever
|
|
||||||
// username is passed to it.
|
|
||||||
// However, DTR 1.4.3 rejects empty password strings under LDAP, in order to disallow anonymous users.
|
|
||||||
req.SetBasicAuth(username, "really?")
|
|
||||||
|
|
||||||
if headers != nil {
|
|
||||||
for header, value := range headers {
|
|
||||||
req.Header.Add(header, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := r.client.HttpClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
if err == http.ErrHandlerTimeout {
|
|
||||||
log.Error("Login timed out to Docker Trusted Registry")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
log.Errorf("There was an error while authenticating: %s", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode == 401 {
|
|
||||||
// Unauthorized
|
|
||||||
log.Warnf("Unauthorized")
|
|
||||||
return nil, auth.ErrUnauthorized
|
|
||||||
} else if resp.StatusCode >= 400 {
|
|
||||||
log.Errorf("Docker Trusted Registry returned an unexpected status code while authenticating: %s", resp.Status)
|
|
||||||
return nil, auth.ErrUnknown
|
|
||||||
}
|
|
||||||
|
|
||||||
rBody, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("couldn't read body: %s", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return rBody, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *V2Registry) GetAuthToken(username, accessType, hostname, reponame string) (string, error) {
|
|
||||||
uri := fmt.Sprintf("%s/auth/token?scope=repository:%s:%s&service=%s", r.RegistryConfig.URL, reponame, accessType, hostname)
|
|
||||||
|
|
||||||
log.Debugf("contacting DTR for auth token: %s", uri)
|
|
||||||
|
|
||||||
data, err := r.doRequest("GET", uri, nil, nil, username)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var token AuthToken
|
|
||||||
if err := json.Unmarshal(data, &token); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return token.Token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *V2Registry) GetConfig() *orca.RegistryConfig {
|
|
||||||
return &r.RegistryConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *V2Registry) GetTransport() http.RoundTripper {
|
|
||||||
return r.client.HttpClient.Transport
|
|
||||||
}
|
|
Loading…
Reference in a new issue