github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/distribution/registry_unit_test.go (about)

     1  package distribution
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"net/url"
     9  	"os"
    10  	"runtime"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/Sirupsen/logrus"
    15  	"github.com/docker/distribution/reference"
    16  	"github.com/docker/docker/api/types"
    17  	registrytypes "github.com/docker/docker/api/types/registry"
    18  	"github.com/docker/docker/pkg/archive"
    19  	"github.com/docker/docker/pkg/stringid"
    20  	"github.com/docker/docker/registry"
    21  	"golang.org/x/net/context"
    22  )
    23  
    24  const secretRegistryToken = "mysecrettoken"
    25  
    26  type tokenPassThruHandler struct {
    27  	reached       bool
    28  	gotToken      bool
    29  	shouldSend401 func(url string) bool
    30  }
    31  
    32  func (h *tokenPassThruHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    33  	h.reached = true
    34  	if strings.Contains(r.Header.Get("Authorization"), secretRegistryToken) {
    35  		logrus.Debug("Detected registry token in auth header")
    36  		h.gotToken = true
    37  	}
    38  	if h.shouldSend401 == nil || h.shouldSend401(r.RequestURI) {
    39  		w.Header().Set("WWW-Authenticate", `Bearer realm="foorealm"`)
    40  		w.WriteHeader(401)
    41  	}
    42  }
    43  
    44  func testTokenPassThru(t *testing.T, ts *httptest.Server) {
    45  	tmp, err := testDirectory("")
    46  	if err != nil {
    47  		t.Fatal(err)
    48  	}
    49  	defer os.RemoveAll(tmp)
    50  
    51  	uri, err := url.Parse(ts.URL)
    52  	if err != nil {
    53  		t.Fatalf("could not parse url from test server: %v", err)
    54  	}
    55  
    56  	endpoint := registry.APIEndpoint{
    57  		Mirror:       false,
    58  		URL:          uri,
    59  		Version:      2,
    60  		Official:     false,
    61  		TrimHostname: false,
    62  		TLSConfig:    nil,
    63  	}
    64  	n, _ := reference.ParseNormalizedNamed("testremotename")
    65  	repoInfo := &registry.RepositoryInfo{
    66  		Name: n,
    67  		Index: &registrytypes.IndexInfo{
    68  			Name:     "testrepo",
    69  			Mirrors:  nil,
    70  			Secure:   false,
    71  			Official: false,
    72  		},
    73  		Official: false,
    74  	}
    75  	imagePullConfig := &ImagePullConfig{
    76  		Config: Config{
    77  			MetaHeaders: http.Header{},
    78  			AuthConfig: &types.AuthConfig{
    79  				RegistryToken: secretRegistryToken,
    80  			},
    81  		},
    82  		Schema2Types: ImageTypes,
    83  	}
    84  	puller, err := newPuller(endpoint, repoInfo, imagePullConfig)
    85  	if err != nil {
    86  		t.Fatal(err)
    87  	}
    88  	p := puller.(*v2Puller)
    89  	ctx := context.Background()
    90  	p.repo, _, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
    91  	if err != nil {
    92  		t.Fatal(err)
    93  	}
    94  
    95  	logrus.Debug("About to pull")
    96  	// We expect it to fail, since we haven't mock'd the full registry exchange in our handler above
    97  	tag, _ := reference.WithTag(n, "tag_goes_here")
    98  	_ = p.pullV2Repository(ctx, tag)
    99  }
   100  
   101  func TestTokenPassThru(t *testing.T) {
   102  	handler := &tokenPassThruHandler{shouldSend401: func(url string) bool { return url == "/v2/" }}
   103  	ts := httptest.NewServer(handler)
   104  	defer ts.Close()
   105  
   106  	testTokenPassThru(t, ts)
   107  
   108  	if !handler.reached {
   109  		t.Fatal("Handler not reached")
   110  	}
   111  	if !handler.gotToken {
   112  		t.Fatal("Failed to receive registry token")
   113  	}
   114  }
   115  
   116  func TestTokenPassThruDifferentHost(t *testing.T) {
   117  	handler := new(tokenPassThruHandler)
   118  	ts := httptest.NewServer(handler)
   119  	defer ts.Close()
   120  
   121  	tsredirect := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   122  		if r.RequestURI == "/v2/" {
   123  			w.Header().Set("WWW-Authenticate", `Bearer realm="foorealm"`)
   124  			w.WriteHeader(401)
   125  			return
   126  		}
   127  		http.Redirect(w, r, ts.URL+r.URL.Path, http.StatusMovedPermanently)
   128  	}))
   129  	defer tsredirect.Close()
   130  
   131  	testTokenPassThru(t, tsredirect)
   132  
   133  	if !handler.reached {
   134  		t.Fatal("Handler not reached")
   135  	}
   136  	if handler.gotToken {
   137  		t.Fatal("Redirect should not forward Authorization header to another host")
   138  	}
   139  }
   140  
   141  // testDirectory creates a new temporary directory and returns its path.
   142  // The contents of directory at path `templateDir` is copied into the
   143  // new directory.
   144  func testDirectory(templateDir string) (dir string, err error) {
   145  	testID := stringid.GenerateNonCryptoID()[:4]
   146  	prefix := fmt.Sprintf("docker-test%s-%s-", testID, getCallerName(2))
   147  	if prefix == "" {
   148  		prefix = "docker-test-"
   149  	}
   150  	dir, err = ioutil.TempDir("", prefix)
   151  	if err = os.Remove(dir); err != nil {
   152  		return
   153  	}
   154  	if templateDir != "" {
   155  		if err = archive.CopyWithTar(templateDir, dir); err != nil {
   156  			return
   157  		}
   158  	}
   159  	return
   160  }
   161  
   162  // getCallerName introspects the call stack and returns the name of the
   163  // function `depth` levels down in the stack.
   164  func getCallerName(depth int) string {
   165  	// Use the caller function name as a prefix.
   166  	// This helps trace temp directories back to their test.
   167  	pc, _, _, _ := runtime.Caller(depth + 1)
   168  	callerLongName := runtime.FuncForPC(pc).Name()
   169  	parts := strings.Split(callerLongName, ".")
   170  	callerShortName := parts[len(parts)-1]
   171  	return callerShortName
   172  }