github.com/apcera/util@v0.0.0-20180322191801-7a50bc84ee48/dockertest/v1/mock_v1_registry.go (about)

     1  // Copyright 2014-2015 Apcera Inc. All rights reserved.
     2  
     3  package v1
     4  
     5  import (
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"net/url"
    12  	"strconv"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/gorilla/mux"
    17  )
    18  
    19  var (
    20  	testVerbose    = false // Change to true in order to see HTTP requests in test output.
    21  	testHttpServer *httptest.Server
    22  	testLayers     = map[string]map[string]string{
    23  		"badcafe": {
    24  			"json":     `{"id":"badcafe","k1": "v1"}`,
    25  			"ancestry": `["badcafe"]`,
    26  			"layer":    string([]byte{0xa1, 0xb2, 0xc3}),
    27  			"checksum": "1ac330d",
    28  		},
    29  		"deadbeef": {
    30  			"json":     `{"id":"deadbeef","k2": "v2"}`,
    31  			"ancestry": `["deadbeef", "badcafe"]`,
    32  			"layer":    string([]byte{0xd4, 0xe5, 0xf6}),
    33  			"checksum": "2bd330f",
    34  		},
    35  		"bd51c4e1b5aceec2ff4bdd87d3fe5f1f16e1120490dee47e2999036f5bc55ccf": { // A random (valid) LayerID
    36  			"json":     `{"id":"bd51c4e1b5aceec2ff4bdd87d3fe5f1f16e1120490dee47e2999036f5bc55ccf","k1": "v1"}`,
    37  			"ancestry": `["bd51c4e1b5aceec2ff4bdd87d3fe5f1f16e1120490dee47e2999036f5bc55ccf"]`,
    38  			"layer":    string([]byte{0xa1, 0xb2, 0xc3}),
    39  			"checksum": "abcd1234",
    40  		},
    41  	}
    42  	testRepositories = map[string]map[string]string{
    43  		"foo/bar": {
    44  			"latest": "deadbeef",
    45  			"base":   "badcafe",
    46  		},
    47  		"some/image": {
    48  			"latest": "bd51c4e1b5aceec2ff4bdd87d3fe5f1f16e1120490dee47e2999036f5bc55ccf",
    49  		},
    50  		"base": {
    51  			"latest": "badcafe",
    52  		},
    53  	}
    54  	mu sync.Mutex
    55  )
    56  
    57  func RunMockRegistry() *httptest.Server {
    58  	mu.Lock()
    59  	defer mu.Unlock()
    60  
    61  	if testHttpServer != nil {
    62  		return testHttpServer
    63  	}
    64  
    65  	r := mux.NewRouter()
    66  	r.HandleFunc("/v1/images/{image_id:[^/]+}/{data_type:json|layer|ancestry}", handlerImage).Methods("GET")
    67  	r.HandleFunc("/v1/repositories/{repository:.+}/tags", handlerTags).Methods("GET")
    68  	r.HandleFunc("/v1/repositories/{repository:.+}/images", handlerImages).Methods("GET")
    69  
    70  	testHttpServer = httptest.NewServer(logHandler(r))
    71  	return testHttpServer
    72  }
    73  
    74  func logHandler(handler http.Handler) http.Handler {
    75  	if !testVerbose {
    76  		return handler
    77  	}
    78  	lh := func(w http.ResponseWriter, r *http.Request) {
    79  		fmt.Printf("%s \"%s %s\"\n", r.RemoteAddr, r.Method, r.URL)
    80  		handler.ServeHTTP(w, r)
    81  	}
    82  	return http.HandlerFunc(lh)
    83  }
    84  
    85  func writeResponse(w http.ResponseWriter, httpStatus int, payload interface{}) {
    86  	w.WriteHeader(httpStatus)
    87  	body, err := json.Marshal(payload)
    88  	if err != nil {
    89  		io.WriteString(w, err.Error())
    90  		return
    91  	}
    92  	w.Write(body)
    93  }
    94  
    95  func checkAuth(w http.ResponseWriter, r *http.Request) bool {
    96  	writeAuth := func() {
    97  		sessionID := fmt.Sprintf("FAKE-SESSION-%d", time.Now().UnixNano())
    98  		token := fmt.Sprintf("FAKE-TOKEN-%d", time.Now().UnixNano())
    99  
   100  		http.SetCookie(w, &http.Cookie{Name: "session", Value: sessionID, MaxAge: 3600})
   101  		w.Header().Add("X-Docker-Token", token)
   102  	}
   103  
   104  	// Mock registry doesn't really perform any auth.
   105  	if len(r.Cookies()) > 0 {
   106  		writeAuth()
   107  		return true
   108  	}
   109  
   110  	if (len(r.Header.Get("Authorization"))) > 0 {
   111  		writeAuth()
   112  		return true
   113  	}
   114  	w.Header().Add("WWW-Authenticate", "token")
   115  	writeResponse(w, 401, "Bad auth")
   116  	return false
   117  }
   118  
   119  func handlerImages(w http.ResponseWriter, r *http.Request) {
   120  	u, _ := url.Parse(testHttpServer.URL) // Ignoring error, URL is guaranteed to be OK.
   121  
   122  	w.Header().Add("X-Docker-Endpoints", fmt.Sprintf("%s     ,    %s", u.Host, "test.example.com"))
   123  	w.Header().Add("X-Docker-Token", fmt.Sprintf("FAKE-SESSION-%d", time.Now().UnixNano()))
   124  
   125  	var images []map[string]string
   126  
   127  	for imageID, layer := range testLayers {
   128  		image := make(map[string]string)
   129  		image["id"] = imageID
   130  		image["checksum"] = layer["checksum"]
   131  		images = append(images, image)
   132  	}
   133  	writeResponse(w, 200, images)
   134  }
   135  
   136  func handlerImage(w http.ResponseWriter, r *http.Request) {
   137  	if !checkAuth(w, r) {
   138  		return
   139  	}
   140  
   141  	vars := mux.Vars(r)
   142  	layer, exists := testLayers[vars["image_id"]]
   143  	if !exists {
   144  		http.NotFound(w, r)
   145  		return
   146  	}
   147  
   148  	layer_size := len(layer["layer"])
   149  	w.Header().Add("X-Docker-Size", strconv.Itoa(layer_size))
   150  	io.WriteString(w, layer[vars["data_type"]])
   151  }
   152  
   153  func handlerTags(w http.ResponseWriter, r *http.Request) {
   154  	if !checkAuth(w, r) {
   155  		return
   156  	}
   157  
   158  	vars := mux.Vars(r)
   159  	tags, exists := testRepositories[vars["repository"]]
   160  	if !exists {
   161  		http.NotFound(w, r)
   162  		return
   163  	}
   164  
   165  	writeResponse(w, 200, tags)
   166  }