github.com/adxhyt/docker@v1.4.2-0.20150117221845-467b7c821390/registry/session_v2.go (about)

     1  package registry
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"net/url"
     9  	"strconv"
    10  
    11  	log "github.com/Sirupsen/logrus"
    12  	"github.com/docker/docker/utils"
    13  	"github.com/gorilla/mux"
    14  )
    15  
    16  func newV2RegistryRouter() *mux.Router {
    17  	router := mux.NewRouter()
    18  
    19  	v2Router := router.PathPrefix("/v2/").Subrouter()
    20  
    21  	// Version Info
    22  	v2Router.Path("/version").Name("version")
    23  
    24  	// Image Manifests
    25  	v2Router.Path("/manifest/{imagename:[a-z0-9-._/]+}/{tagname:[a-zA-Z0-9-._]+}").Name("manifests")
    26  
    27  	// List Image Tags
    28  	v2Router.Path("/tags/{imagename:[a-z0-9-._/]+}").Name("tags")
    29  
    30  	// Download a blob
    31  	v2Router.Path("/blob/{imagename:[a-z0-9-._/]+}/{sumtype:[a-z0-9._+-]+}/{sum:[a-fA-F0-9]{4,}}").Name("downloadBlob")
    32  
    33  	// Upload a blob
    34  	v2Router.Path("/blob/{imagename:[a-z0-9-._/]+}/{sumtype:[a-z0-9._+-]+}").Name("uploadBlob")
    35  
    36  	// Mounting a blob in an image
    37  	v2Router.Path("/mountblob/{imagename:[a-z0-9-._/]+}/{sumtype:[a-z0-9._+-]+}/{sum:[a-fA-F0-9]{4,}}").Name("mountBlob")
    38  
    39  	return router
    40  }
    41  
    42  // APIVersion2 /v2/
    43  var v2HTTPRoutes = newV2RegistryRouter()
    44  
    45  func getV2URL(e *Endpoint, routeName string, vars map[string]string) (*url.URL, error) {
    46  	route := v2HTTPRoutes.Get(routeName)
    47  	if route == nil {
    48  		return nil, fmt.Errorf("unknown regisry v2 route name: %q", routeName)
    49  	}
    50  
    51  	varReplace := make([]string, 0, len(vars)*2)
    52  	for key, val := range vars {
    53  		varReplace = append(varReplace, key, val)
    54  	}
    55  
    56  	routePath, err := route.URLPath(varReplace...)
    57  	if err != nil {
    58  		return nil, fmt.Errorf("unable to make registry route %q with vars %v: %s", routeName, vars, err)
    59  	}
    60  	u, err := url.Parse(REGISTRYSERVER)
    61  	if err != nil {
    62  		return nil, fmt.Errorf("invalid registry url: %s", err)
    63  	}
    64  
    65  	return &url.URL{
    66  		Scheme: u.Scheme,
    67  		Host:   u.Host,
    68  		Path:   routePath.Path,
    69  	}, nil
    70  }
    71  
    72  // V2 Provenance POC
    73  
    74  func (r *Session) GetV2Version(token []string) (*RegistryInfo, error) {
    75  	routeURL, err := getV2URL(r.indexEndpoint, "version", nil)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	method := "GET"
    81  	log.Debugf("[registry] Calling %q %s", method, routeURL.String())
    82  
    83  	req, err := r.reqFactory.NewRequest(method, routeURL.String(), nil)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	setTokenAuth(req, token)
    88  	res, _, err := r.doRequest(req)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	defer res.Body.Close()
    93  	if res.StatusCode != 200 {
    94  		return nil, utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d fetching Version", res.StatusCode), res)
    95  	}
    96  
    97  	decoder := json.NewDecoder(res.Body)
    98  	versionInfo := new(RegistryInfo)
    99  
   100  	err = decoder.Decode(versionInfo)
   101  	if err != nil {
   102  		return nil, fmt.Errorf("unable to decode GetV2Version JSON response: %s", err)
   103  	}
   104  
   105  	return versionInfo, nil
   106  }
   107  
   108  //
   109  // 1) Check if TarSum of each layer exists /v2/
   110  //  1.a) if 200, continue
   111  //  1.b) if 300, then push the
   112  //  1.c) if anything else, err
   113  // 2) PUT the created/signed manifest
   114  //
   115  func (r *Session) GetV2ImageManifest(imageName, tagName string, token []string) ([]byte, error) {
   116  	vars := map[string]string{
   117  		"imagename": imageName,
   118  		"tagname":   tagName,
   119  	}
   120  
   121  	routeURL, err := getV2URL(r.indexEndpoint, "manifests", vars)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	method := "GET"
   127  	log.Debugf("[registry] Calling %q %s", method, routeURL.String())
   128  
   129  	req, err := r.reqFactory.NewRequest(method, routeURL.String(), nil)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	setTokenAuth(req, token)
   134  	res, _, err := r.doRequest(req)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	defer res.Body.Close()
   139  	if res.StatusCode != 200 {
   140  		if res.StatusCode == 401 {
   141  			return nil, errLoginRequired
   142  		} else if res.StatusCode == 404 {
   143  			return nil, ErrDoesNotExist
   144  		}
   145  		return nil, utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to fetch for %s:%s", res.StatusCode, imageName, tagName), res)
   146  	}
   147  
   148  	buf, err := ioutil.ReadAll(res.Body)
   149  	if err != nil {
   150  		return nil, fmt.Errorf("Error while reading the http response: %s", err)
   151  	}
   152  	return buf, nil
   153  }
   154  
   155  // - Succeeded to mount for this image scope
   156  // - Failed with no error (So continue to Push the Blob)
   157  // - Failed with error
   158  func (r *Session) PostV2ImageMountBlob(imageName, sumType, sum string, token []string) (bool, error) {
   159  	vars := map[string]string{
   160  		"imagename": imageName,
   161  		"sumtype":   sumType,
   162  		"sum":       sum,
   163  	}
   164  
   165  	routeURL, err := getV2URL(r.indexEndpoint, "mountBlob", vars)
   166  	if err != nil {
   167  		return false, err
   168  	}
   169  
   170  	method := "POST"
   171  	log.Debugf("[registry] Calling %q %s", method, routeURL.String())
   172  
   173  	req, err := r.reqFactory.NewRequest(method, routeURL.String(), nil)
   174  	if err != nil {
   175  		return false, err
   176  	}
   177  	setTokenAuth(req, token)
   178  	res, _, err := r.doRequest(req)
   179  	if err != nil {
   180  		return false, err
   181  	}
   182  	res.Body.Close() // close early, since we're not needing a body on this call .. yet?
   183  	switch res.StatusCode {
   184  	case 200:
   185  		// return something indicating no push needed
   186  		return true, nil
   187  	case 300:
   188  		// return something indicating blob push needed
   189  		return false, nil
   190  	}
   191  	return false, fmt.Errorf("Failed to mount %q - %s:%s : %d", imageName, sumType, sum, res.StatusCode)
   192  }
   193  
   194  func (r *Session) GetV2ImageBlob(imageName, sumType, sum string, blobWrtr io.Writer, token []string) error {
   195  	vars := map[string]string{
   196  		"imagename": imageName,
   197  		"sumtype":   sumType,
   198  		"sum":       sum,
   199  	}
   200  
   201  	routeURL, err := getV2URL(r.indexEndpoint, "downloadBlob", vars)
   202  	if err != nil {
   203  		return err
   204  	}
   205  
   206  	method := "GET"
   207  	log.Debugf("[registry] Calling %q %s", method, routeURL.String())
   208  	req, err := r.reqFactory.NewRequest(method, routeURL.String(), nil)
   209  	if err != nil {
   210  		return err
   211  	}
   212  	setTokenAuth(req, token)
   213  	res, _, err := r.doRequest(req)
   214  	if err != nil {
   215  		return err
   216  	}
   217  	defer res.Body.Close()
   218  	if res.StatusCode != 200 {
   219  		if res.StatusCode == 401 {
   220  			return errLoginRequired
   221  		}
   222  		return utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to pull %s blob", res.StatusCode, imageName), res)
   223  	}
   224  
   225  	_, err = io.Copy(blobWrtr, res.Body)
   226  	return err
   227  }
   228  
   229  func (r *Session) GetV2ImageBlobReader(imageName, sumType, sum string, token []string) (io.ReadCloser, int64, error) {
   230  	vars := map[string]string{
   231  		"imagename": imageName,
   232  		"sumtype":   sumType,
   233  		"sum":       sum,
   234  	}
   235  
   236  	routeURL, err := getV2URL(r.indexEndpoint, "downloadBlob", vars)
   237  	if err != nil {
   238  		return nil, 0, err
   239  	}
   240  
   241  	method := "GET"
   242  	log.Debugf("[registry] Calling %q %s", method, routeURL.String())
   243  	req, err := r.reqFactory.NewRequest(method, routeURL.String(), nil)
   244  	if err != nil {
   245  		return nil, 0, err
   246  	}
   247  	setTokenAuth(req, token)
   248  	res, _, err := r.doRequest(req)
   249  	if err != nil {
   250  		return nil, 0, err
   251  	}
   252  	if res.StatusCode != 200 {
   253  		if res.StatusCode == 401 {
   254  			return nil, 0, errLoginRequired
   255  		}
   256  		return nil, 0, utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to pull %s blob", res.StatusCode, imageName), res)
   257  	}
   258  	lenStr := res.Header.Get("Content-Length")
   259  	l, err := strconv.ParseInt(lenStr, 10, 64)
   260  	if err != nil {
   261  		return nil, 0, err
   262  	}
   263  
   264  	return res.Body, l, err
   265  }
   266  
   267  // Push the image to the server for storage.
   268  // 'layer' is an uncompressed reader of the blob to be pushed.
   269  // The server will generate it's own checksum calculation.
   270  func (r *Session) PutV2ImageBlob(imageName, sumType string, blobRdr io.Reader, token []string) (serverChecksum string, err error) {
   271  	vars := map[string]string{
   272  		"imagename": imageName,
   273  		"sumtype":   sumType,
   274  	}
   275  
   276  	routeURL, err := getV2URL(r.indexEndpoint, "uploadBlob", vars)
   277  	if err != nil {
   278  		return "", err
   279  	}
   280  
   281  	method := "PUT"
   282  	log.Debugf("[registry] Calling %q %s", method, routeURL.String())
   283  	req, err := r.reqFactory.NewRequest(method, routeURL.String(), blobRdr)
   284  	if err != nil {
   285  		return "", err
   286  	}
   287  	setTokenAuth(req, token)
   288  	res, _, err := r.doRequest(req)
   289  	if err != nil {
   290  		return "", err
   291  	}
   292  	defer res.Body.Close()
   293  	if res.StatusCode != 201 {
   294  		if res.StatusCode == 401 {
   295  			return "", errLoginRequired
   296  		}
   297  		return "", utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to push %s blob", res.StatusCode, imageName), res)
   298  	}
   299  
   300  	type sumReturn struct {
   301  		Checksum string `json:"checksum"`
   302  	}
   303  
   304  	decoder := json.NewDecoder(res.Body)
   305  	var sumInfo sumReturn
   306  
   307  	err = decoder.Decode(&sumInfo)
   308  	if err != nil {
   309  		return "", fmt.Errorf("unable to decode PutV2ImageBlob JSON response: %s", err)
   310  	}
   311  
   312  	// XXX this is a json struct from the registry, with its checksum
   313  	return sumInfo.Checksum, nil
   314  }
   315  
   316  // Finally Push the (signed) manifest of the blobs we've just pushed
   317  func (r *Session) PutV2ImageManifest(imageName, tagName string, manifestRdr io.Reader, token []string) error {
   318  	vars := map[string]string{
   319  		"imagename": imageName,
   320  		"tagname":   tagName,
   321  	}
   322  
   323  	routeURL, err := getV2URL(r.indexEndpoint, "manifests", vars)
   324  	if err != nil {
   325  		return err
   326  	}
   327  
   328  	method := "PUT"
   329  	log.Debugf("[registry] Calling %q %s", method, routeURL.String())
   330  	req, err := r.reqFactory.NewRequest(method, routeURL.String(), manifestRdr)
   331  	if err != nil {
   332  		return err
   333  	}
   334  	setTokenAuth(req, token)
   335  	res, _, err := r.doRequest(req)
   336  	if err != nil {
   337  		return err
   338  	}
   339  	res.Body.Close()
   340  	if res.StatusCode != 201 {
   341  		if res.StatusCode == 401 {
   342  			return errLoginRequired
   343  		}
   344  		return utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to push %s:%s manifest", res.StatusCode, imageName, tagName), res)
   345  	}
   346  
   347  	return nil
   348  }
   349  
   350  // Given a repository name, returns a json array of string tags
   351  func (r *Session) GetV2RemoteTags(imageName string, token []string) ([]string, error) {
   352  	vars := map[string]string{
   353  		"imagename": imageName,
   354  	}
   355  
   356  	routeURL, err := getV2URL(r.indexEndpoint, "tags", vars)
   357  	if err != nil {
   358  		return nil, err
   359  	}
   360  
   361  	method := "GET"
   362  	log.Debugf("[registry] Calling %q %s", method, routeURL.String())
   363  
   364  	req, err := r.reqFactory.NewRequest(method, routeURL.String(), nil)
   365  	if err != nil {
   366  		return nil, err
   367  	}
   368  	setTokenAuth(req, token)
   369  	res, _, err := r.doRequest(req)
   370  	if err != nil {
   371  		return nil, err
   372  	}
   373  	defer res.Body.Close()
   374  	if res.StatusCode != 200 {
   375  		if res.StatusCode == 401 {
   376  			return nil, errLoginRequired
   377  		} else if res.StatusCode == 404 {
   378  			return nil, ErrDoesNotExist
   379  		}
   380  		return nil, utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to fetch for %s", res.StatusCode, imageName), res)
   381  	}
   382  
   383  	decoder := json.NewDecoder(res.Body)
   384  	var tags []string
   385  	err = decoder.Decode(&tags)
   386  	if err != nil {
   387  		return nil, fmt.Errorf("Error while decoding the http response: %s", err)
   388  	}
   389  	return tags, nil
   390  }