Navigation Menu

Skip to content

Commit

Permalink
use old version docker and auth to dockerhub (#3)
Browse files Browse the repository at this point in the history
* add dockerhuboption

* fix dependency

* fix docker auth

* update go1.13
  • Loading branch information
tomoyamachi committed Nov 4, 2019
1 parent 9894f20 commit 1389279
Show file tree
Hide file tree
Showing 9 changed files with 240 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
@@ -1,7 +1,7 @@
defaults: &defaults
working_directory: /go/src/github.com/goodwithtech/dockertags
docker:
- image: circleci/golang:1.12.3
- image: circleci/golang:1.13
environment:
GO111MODULE: "on"
steps:
Expand Down
19 changes: 13 additions & 6 deletions go.mod
@@ -1,15 +1,25 @@
module github.com/goodwithtech/dockertags

go 1.12
go 1.13

replace github.com/docker/cli => github.com/docker/cli v0.0.0-20180920165730-54c19e67f69c

require (
cloud.google.com/go v0.37.4 // indirect
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0
github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/aws/aws-sdk-go v1.23.17
github.com/docker/cli v0.0.0-20190906153656-016a3232168d
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 // indirect
github.com/docker/cli v0.0.0-20180920165730-54c19e67f69c
github.com/docker/distribution v2.7.1+incompatible
github.com/docker/docker v1.13.1 // indirect
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f
github.com/docker/docker-credential-helpers v0.6.3 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect
github.com/moul/http2curl v1.0.0 // indirect
Expand All @@ -18,14 +28,11 @@ require (
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runc v0.1.1 // indirect
github.com/parnurzeal/gorequest v0.2.15
github.com/pkg/errors v0.8.1 // indirect
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
github.com/stretchr/testify v1.3.0 // indirect
github.com/urfave/cli v1.22.0
go.uber.org/atomic v1.4.0 // indirect
go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.10.0
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 // indirect
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 // indirect
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6
gotest.tools v2.1.0+incompatible // indirect
Expand Down
66 changes: 60 additions & 6 deletions go.sum

Large diffs are not rendered by default.

39 changes: 27 additions & 12 deletions pkg/auth/auth.go
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/goodwithtech/dockertags/pkg/log"

"github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/types"
"github.com/docker/docker/api/types"
)

const (
Expand Down Expand Up @@ -50,31 +50,31 @@ func GetAuthConfig(username, password, registry string) (types.AuthConfig, error
// if they passed a specific registry, return those creds _if_ they exist
if registry != "" {
if creds, ok := authConfigs[registry]; ok {
fixAuthConfig(&creds, registry)
return creds, nil
c := fixAuthConfig(creds, registry)
return c, nil
}

if strings.HasPrefix(registry, "https://") {
registryCleaned := strings.TrimPrefix(registry, "https://")
if creds, ok := authConfigs[registryCleaned]; ok {
fixAuthConfig(&creds, registryCleaned)
return creds, nil
c := fixAuthConfig(creds, registryCleaned)
return c, nil
}
}

if strings.HasPrefix(registry, "http://") {
registryCleaned := strings.TrimPrefix(registry, "http://")
if creds, ok := authConfigs[registryCleaned]; ok {
fixAuthConfig(&creds, registryCleaned)
return creds, nil
c := fixAuthConfig(creds, registryCleaned)
return c, nil
}
}

if !strings.HasPrefix(registry, "https://") && !strings.HasPrefix(registry, "http://") {
registryCleaned := "https://" + registry
if creds, ok := authConfigs[registryCleaned]; ok {
fixAuthConfig(&creds, registryCleaned)
return creds, nil
c := fixAuthConfig(creds, registryCleaned)
return c, nil
}
}

Expand All @@ -88,17 +88,32 @@ func GetAuthConfig(username, password, registry string) (types.AuthConfig, error
// found in the auth config.
for _, creds := range authConfigs {
log.Logger.Debugf("No registry passed. Using registry %q\n", creds.ServerAddress)
return creds, nil
c := fixAuthConfig(creds, creds.ServerAddress)
return c, nil
}

log.Logger.Debug("Not using any authentication")
return types.AuthConfig{}, nil
}

func fixAuthConfig(creds *types.AuthConfig, registry string) {
// fixAuthConfig overwrites the AuthConfig's ServerAddress field with the
// registry value if ServerAddress is empty. For example, config.Load() will
// return AuthConfigs with empty ServerAddresses if the configuration file
// contains only an "credsHelper" object.
func fixAuthConfig(creds types.AuthConfig, registry string) (c types.AuthConfig) {
c.Username = creds.Username
c.Password = creds.Password
c.Auth = creds.Auth
c.Email = creds.Email
c.IdentityToken = creds.IdentityToken
c.RegistryToken = creds.RegistryToken

c.ServerAddress = creds.ServerAddress
if creds.ServerAddress == "" {
creds.ServerAddress = registry
c.ServerAddress = registry
}

return c
}

func setDefaultRegistry(auth types.AuthConfig) types.AuthConfig {
Expand Down
44 changes: 44 additions & 0 deletions pkg/provider/dockerhub/auth.go
@@ -0,0 +1,44 @@
package dockerhub

import (
"context"
"encoding/json"
"net/http"
"time"

"github.com/docker/docker/api/types"
)

func getJSON(ctx context.Context, url string, auth types.AuthConfig, timeout time.Duration, response interface{}) (http.Header, error) {
cli, err := new(auth, timeout)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
resp, err := cli.Do(req.WithContext(ctx))
if err != nil {
return nil, err
}
defer resp.Body.Close()

if err := json.NewDecoder(resp.Body).Decode(response); err != nil {
return nil, err
}

return resp.Header, nil
}

func new(auth types.AuthConfig, timeout time.Duration) (*http.Client, error) {
transport := http.DefaultTransport
tokenTransport := &DockerhubTokenTransport{
Transport: transport,
Username: auth.Username,
Password: auth.Password,
}

registry := &http.Client{
Timeout: timeout,
Transport: tokenTransport,
}
return registry, nil
}
22 changes: 10 additions & 12 deletions pkg/provider/dockerhub/dockerhub.go
Expand Up @@ -2,6 +2,7 @@ package dockerhub

import (
"context"
"fmt"

"golang.org/x/sync/errgroup"

Expand All @@ -10,12 +11,13 @@ import (
"github.com/goodwithtech/dockertags/pkg/log"
"github.com/goodwithtech/dockertags/pkg/types"

dockertypes "github.com/docker/cli/cli/config/types"
dockertypes "github.com/docker/docker/api/types"

"github.com/goodwithtech/dockertags/pkg/registry"
)

const TAG_PER_PAGE = 10
const registryURL = "https://registry.hub.docker.com/"

type DockerHub struct {
registry *registry.Registry
Expand All @@ -38,15 +40,11 @@ type ImageSummary struct {
func (p *DockerHub) Run(ctx context.Context, domain, repository string, option types.RequestOption) (types.ImageTags, error) {
auth := dockertypes.AuthConfig{
ServerAddress: "registry.hub.docker.com",
Username: option.UserName,
Password: option.Password,
}
opt := registry.Opt{}
r, err := registry.New(ctx, auth, opt)
if err != nil {
return nil, err
}

// 1ページ目は普通に取得
tagResp, err := getTagResponse(ctx, r, repository, 1)
tagResp, err := getTagResponse(ctx, auth, option.Timeout, repository, 1)
if err != nil {
return nil, err
}
Expand All @@ -59,7 +57,7 @@ func (p *DockerHub) Run(ctx context.Context, domain, repository string, option t
for page := 2; page < maxPage; page++ {
page := page
eg.Go(func() error {
tagResp, err := getTagResponse(ctx, r, repository, page)
tagResp, err := getTagResponse(ctx, auth, option.Timeout, repository, page)
if err != nil {
return err
}
Expand Down Expand Up @@ -97,11 +95,11 @@ func convertResultToTag(summaries []ImageSummary) types.ImageTags {
}

// getTagResponse returns the tags for a specific repository.
func getTagResponse(ctx context.Context, r *registry.Registry, repository string, page int) (tagsResponse, error) {
url := r.Url("/v2/repositories/%s/tags/?page=%d", repository, page)
func getTagResponse(ctx context.Context, auth dockertypes.AuthConfig, timeout time.Duration, repository string, page int) (tagsResponse, error) {
url := fmt.Sprintf("%s/v2/repositories/%s/tags/?page=%d", registryURL, repository, page)
log.Logger.Debugf("url=%s,repository=%s", url, repository)
var response tagsResponse
if _, err := r.GetJSON(ctx, url, &response); err != nil {
if _, err := getJSON(ctx, url, auth, timeout, &response); err != nil {
return response, err
}

Expand Down
83 changes: 83 additions & 0 deletions pkg/provider/dockerhub/transport.go
@@ -0,0 +1,83 @@
package dockerhub

import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
)

const authURL = "https://hub.docker.com/v2/users/login/"

type authToken struct {
Token string `json:"token"`
}

type DockerhubTokenTransport struct {
Transport http.RoundTripper
Username string
Password string
}

// RoundTrip defines the round tripper for token transport.
func (t *DockerhubTokenTransport) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := t.Transport.RoundTrip(req)
if err != nil {
return resp, err
}
if t.Username == "" || t.Password == "" {
return resp, nil
}
if !isTokenDemand(resp) {
return resp, nil
}
resp.Body.Close()
return t.authAndRetry(req)
}

func isTokenDemand(resp *http.Response) bool {
if resp == nil {
return false
}
if resp.StatusCode == http.StatusNotFound {
return true
}
return false
}

func (t *DockerhubTokenTransport) authAndRetry(req *http.Request) (*http.Response, error) {
token, authResp, err := t.auth(req.Context())
if err != nil {
return authResp, err
}

response, err := t.retry(req, token)
if response != nil {
response.Header.Set("request-token", token)
}
return response, err
}

func (t *DockerhubTokenTransport) retry(req *http.Request, token string) (*http.Response, error) {
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
return t.Transport.RoundTrip(req)
}

func (t *DockerhubTokenTransport) auth(ctx context.Context) (string, *http.Response, error) {
jsonStr := []byte(fmt.Sprintf(`{"username": "%s","password": "%s"}`, t.Username, t.Password))
resp, err := http.Post(authURL, "application/json", bytes.NewBuffer(jsonStr))
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
}
token := authToken.Token
return token, nil, nil
}
2 changes: 1 addition & 1 deletion pkg/provider/gcr/gcr.go
Expand Up @@ -14,7 +14,7 @@ import (
"github.com/GoogleCloudPlatform/docker-credential-gcr/config"
"github.com/GoogleCloudPlatform/docker-credential-gcr/credhelper"
"github.com/GoogleCloudPlatform/docker-credential-gcr/store"
dockertypes "github.com/docker/cli/cli/config/types"
dockertypes "github.com/docker/docker/api/types"
"github.com/goodwithtech/dockertags/pkg/registry"
)

Expand Down
2 changes: 1 addition & 1 deletion pkg/registry/registry.go
Expand Up @@ -10,9 +10,9 @@ import (
"strings"
"time"

"github.com/docker/cli/cli/config/types"
"github.com/docker/distribution/manifest/manifestlist"
"github.com/docker/distribution/manifest/schema2"
"github.com/docker/docker/api/types"
)

// Registry defines the client for retrieving information from the registry API.
Expand Down

0 comments on commit 1389279

Please sign in to comment.