diff --git a/docs/mock/registry.go b/docs/mock/registry.go deleted file mode 100644 index aad45e39..00000000 --- a/docs/mock/registry.go +++ /dev/null @@ -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 -} diff --git a/docs/readme.md b/docs/readme.md deleted file mode 100644 index 668ebf78..00000000 --- a/docs/readme.md +++ /dev/null @@ -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) -} -``` diff --git a/docs/v1/error.go b/docs/v1/error.go deleted file mode 100644 index 769671a8..00000000 --- a/docs/v1/error.go +++ /dev/null @@ -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) -} diff --git a/docs/v1/registry.go b/docs/v1/registry.go deleted file mode 100644 index 103faea6..00000000 --- a/docs/v1/registry.go +++ /dev/null @@ -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 -} diff --git a/docs/v1/repository.go b/docs/v1/repository.go deleted file mode 100644 index 6f6ca431..00000000 --- a/docs/v1/repository.go +++ /dev/null @@ -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"` - } -) diff --git a/docs/v1/search.go b/docs/v1/search.go deleted file mode 100644 index 084f8f98..00000000 --- a/docs/v1/search.go +++ /dev/null @@ -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"` - } -) diff --git a/docs/v2/registry.go b/docs/v2/registry.go deleted file mode 100644 index 2188f019..00000000 --- a/docs/v2/registry.go +++ /dev/null @@ -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 -}